HP-48/49/50G : Quand le RPL rencontre APL

Ici, on fait dans le petit, le LCD qui déchire sa race, on y cause même calculatrices quand on est en manque !

Modérateur : Politburo

Avatar du membre
Schraf
Fonctionne à 1200 bauds
Fonctionne à 1200 bauds
Messages : 499
Enregistré le : 05 mars 2020 20:45
Contact :

HP-48/49/50G : Quand le RPL rencontre APL

Message par Schraf »

Bonjour à tous,

Suite à un post de FLISZT, je vous propose de reprendre quelques éléments trouvés dans le document de Norman Brenner. Il s’agit de traduire certaines des fonctions APL (sélection, réduction, transformation…) en RPL pour les calculatrices HP-48/49/50G

Création d’un répertoire APL sur les HP

Code : Tout sélectionner

HP 48G : ↱ MEMORY  NEW
HP 49/50 : ↰ FILES

Puis :

NAME : APL
☑ DIRECTORY
OK
Ou encore en tapant la commande :

Code : Tout sélectionner

'APL' ALPHA ALPHA CRDIR
Pour se déplacer dans le répertoire {HOME APL} :

Code : Tout sélectionner

VAR APL
Pour revenir à la racine {HOME} :

Code : Tout sélectionner

HP 48G : ↱ HOME
HP 49/50G : ↰ UPDIR (ENTER sur HP 49)
Par la suite, nous travaillerons dans le répertoire {HOME APL}

Exemple 1 – Compression logique

Ecrire un programme RECUS qui à partir d’une liste (vecteur en APL) de notes d’étudiants renvoie la liste des notes ≥ 10.

Résultat attendu en APL

Code : Tout sélectionner

RECUS 5 12 13 8 17 2
12 13 17
Résultat attendu en RPL

Code : Tout sélectionner

1: {5 12 13 8 17 2}
VAR RECUS
1: {12 13 17}
En APL, la compression logique est une fonction dyadique (2 arguments), celui de gauche est un vecteur logique (1 ou 0) et celui de droite un vecteur quelconque. Le résultat sera le vecteur composé uniquement des éléments associés aux 1. Exemple :

Code : Tout sélectionner

1 0 1 / 5 12 13
5 13            ⍝ On garde 1er et 3e éléments
Vous pouvez tester la commande en direct ici.

Voici la traduction proposée par Norman Brenner pour la Compression Logique que nous noterons CL. (BSEL dans le document initial) :

Code : Tout sélectionner

« 2 « IF NOT THEN DROP END » DOLIST »
'CL. STO          # On mémorise le programme
Avant les explications et une simplification, voyons son utilisation :

Code : Tout sélectionner

2: {5 12 13 8 17 2}  # Notes au niveau 2
1: {0 1 1 0 1 0}     # Masque booléen niveau 1
VAR CL.              # On lance le programme

1: {12. 13. 17.}     # Résultat au niveau 1
DOLIST permet d’appliquer une fonction ou un programme à un groupe de listes. Il faut pour cela saisir des listes, le nombre de listes à traiter et le programme ou la fonction à exécuter.

Le programme CL. indique qu’il y a 2 listes à traiter. Il prend ensuite la négation de la liste du niveau 1 (Ainsi les 0 deviennent des 1 et réciproquement) et applique DROP pour éliminer les éléments correspondant à 1. Voici 2 exemples illustrant l’utilisation de NOT et DOLIST :

Code : Tout sélectionner

{0 1 1 0 1 0}    # Liste de booléens 
ALPHA ALPHA NOT
{1 0 0 1 0 1}    # Négation

4: {5 3 1 4}     # Les listes à traiter
3: {7 2 8 2}
2: 2             # Nb de listes
1: « MAX »       # Fonction à appliquer
PRG LIST PROC DOLIST

1: {7. 3. 8. 4.} # Max entre les 2 listes
Nous pouvons maintenant créer notre programme RECUS, pour cela il faut fournir à CL. la liste des notes ainsi qu’un « vecteur » logique précisant lesquelles sont ≥ 10. Pour cela on duplique la liste et on effectue le test 10 ≥ :

Code : Tout sélectionner

« DUP 10 ≥ CL.»   
'RECUS STO        # On mémorise le programme
En APL, le programme pourrait être :

Code : Tout sélectionner

RECUS ← {(⍵ ≥ 10) / ⍵}
RECUS 5 12 13 8 17 2
Output : 12 13 17
On peut également utiliser IFT pour sélectionner les éléments d’une liste et enlever les autres, en effet :

Code : Tout sélectionner

1: {0  1  1  0  1 0}
2: {5 12 13  8 17 2}
PRG BRCH NXT IFT
1: {12 13 17}
Ainsi notre programme RECUS peut s’écrire plus simplement :

Code : Tout sélectionner

« DUP 10 ≥ SWAP IFT »
'RECUS STO
On duplique la liste (DUP), on crée le vecteur logique (10 ≥), on met le vecteur logique au niveau 2 (SWAP) et on applique IFT.

Ceci donne l’idée de simplifier le code CL. proposé par Norman Brenner par :

Code : Tout sélectionner

« SWAP IFT »
'CL. STO
Remarquons que l’on peut enchainer les compressions, par exemple sélectionnons les notes > 8 ET < 13 :

Code : Tout sélectionner

« DUP 8 > CL. DUP 13 < CL.»
'RAT STO      # Etudiants allant au rattrapage

1: {5 12 13 8 17 2}
VAR RATT
1: {12}
La difficulté est donc principalement de créer le vecteur logique. Pour terminer cette première partie voici un second exemple où l’on veut supprimer les valeurs d’une liste qui ne sont pas entières :

Code : Tout sélectionner

1: { 1 2.5 3.7 8 4}
VAR ENT
1: {1 8 4}
On peut par exemple créer le vecteur logique en testant si le nombre n’est pas plus grand que sa partie entière, ce qui donne :

Code : Tout sélectionner

« DUP DUP IP > NOT CL.»   
'ENT STO
La version a priori plus logique : IP == (Est-ce que le nombre est égal à sa partie entière) ne fonctionne pas car la comparaison ne s’effectue plus élément par élément mais entre les listes, le résultat renvoyé est 0 ou 1 et non plus un vecteur logique. Si quelqu’un a une idée...

En APL :

Code : Tout sélectionner

ENT ← {(⍵ = ⌊⍵) / ⍵}
ENT 1 2.5 3.7 8 4
Output : 1 8 4
Modifié en dernier par Schraf le 21 mars 2022 19:20, modifié 3 fois.
Avatar du membre
Schraf
Fonctionne à 1200 bauds
Fonctionne à 1200 bauds
Messages : 499
Enregistré le : 05 mars 2020 20:45
Contact :

Re: HP-48/49/50G : Quand le RPL rencontre APL

Message par Schraf »

APL ET RPL : RÉDUCTION

En APL, l’association du symbole de réduction / et d’une fonction dyadique permet d’appliquer cette dernière entre tous les éléments de l’opérande (terme de droite). Exemples :

Code : Tout sélectionner

+/ 1 2 3 4
Output : 10      ⍝ (1 + (2 + (3 + 4)))

⌈/ 1 5 2 4       ⍝ x ⌈ y <=> max(x,y)
Output : 5       ⍝ max(1, max(5, max(2,4)))
On a un équivalent plutôt élégant en RPL avec STREAM :

Code : Tout sélectionner

2: { 1 2 3 4 }       # L'opérande
1: « + »             # La fonction à appliquer
PRG LIST PROC STREAM

1: 10                # (((1 + 2) + 3) + 4)

2: { 1 5 2 4 }
1: « MAX »
STREAM

1: 5                 # max(max(max(1, 5), 2), 4)
Attention, en APL les calculs se font de la droite vers la gauche, alors qu’en RPL ils se font de la gauche vers la droite :

Code : Tout sélectionner

2: { 1 2 3 4 }
1: « - »         # Soustraction
STREAM

1: -8            # (((1 - 2) - 3) - 4)

2: { 4 3 2 1 }
1: « - »         # Soustraction
STREAM

1: -2            # (1 - (2 - (3 - 4)))
et en APL :

-/ 1 2 3 4
¯2               ⍝ (1 - (2 - (3 - 4)))
Il est également possible en APL et préciser le nombre d’éléments à traiter lors de chaque réduction, par exemple :

Code : Tout sélectionner

2 +/ 1 2 3 4 5
3 5 7 9       ⍝ Par 2 : 1+2=3, 2+3=5, 3+4=7...
Et bien c’est également possible en RPL avec DOSUBS :

Code : Tout sélectionner

3: {1 2 3 4 5}
2: 2             # Traiter les éléments 2 par 2 
1: « + »         # Opération pour la réduction
PRG LIST PROC DOSUB

1: {3. 5. 7.}
Dans le cas où le nombre d’éléments à traiter est égal à 1, cela revient finalement à faire un MAP, c’est-à-dire une transformation terme à terme des éléments de la suite.

Remarquons aussi que dans ce cas, DOSUBS et DOLIST ont le même effet :

Code : Tout sélectionner

2: {9 10 12}
1: « 1 « SQ » DOSUB »   # Mettre au carré 1 par 1
EVAL

1: {81. 100. 144.}

2: {9 10 12}
1: « 1 « SQ » DOLIST »  # Mettre au carré 1 liste
EVAL

1: {81. 100. 144.}
A priori pas d’intérêt car { 9 10 12 } SQ donne le même résultat, cependant regardons cet exemple tiré d’une de mes vidéos sur l’APL

Exemple 2 – Meilleurs score

Récupérer les meilleurs scores de 3 équipes :

Code : Tout sélectionner

BEST (4 8 9) (5 10 3) (7 12)  ⍝ 3 équipes
9 10 12      ⍝ Meilleurs score de chaque équipe
On voudrait cet équivalent en RPL :

Code : Tout sélectionner

1: {{4 8 9} {5 10 3} {7 12}}
VAR BEST

1: {9. 10. 12.}
Il faut donc que l’on prenne le MAX de CHAQUE liste, c’est à la fois une réduction par le maximum et un mapping. D’où simplement :

Code : Tout sélectionner

« 1 « « MAX » STREAM » DOSUBS »
'BEST STO
Exemple 3 – Combien sont plus grands que moi ?

Toujours tiré d’une de mes vidéos sur l’APL, on voudrait connaitre, pour chaque élément d’une liste, combien sont plus grands. Par exemple :

Code : Tout sélectionner

{1 30 4 16 5 1 8}
VAR SUP
{5. 0. 4. 1. 3. 5. 2.}
Ce qui signifie que 5 valeurs sont strictement plus grandes que 1, aucune n’est plus grande que 30, 4 sont strictement plus grandes que 4, etc.

Chaque élément doit être comparé à l’ensemble de la liste, ce qui va créer un vecteur logique dont il faudra additionner les termes (réduction). Par exemple

Code : Tout sélectionner

1 < {1 30 4 16 5 1 8}

{0. 1. 1. 1. 1. 0. 1.}
dont la somme fait 5. Cette addition peut se faire par un

Code : Tout sélectionner

« + » STREAM
ou bien sûr par ∑LIST.

Finalement, le programme SUP peut s’écrire :

Code : Tout sélectionner

« DUP →s           # Copie de la liste dans s
 « 1               # Début du mapping
  « s < ∑LIST      # Vecteur booléen et réduction
  » DOSUBS         # ou DOLIST
 »
»
'SUP STO
Il y a plusieurs solutions en APL (voir vidéo) mais celle analogue à la version RPL est :

Code : Tout sélectionner

SUP ← {{+/ ⍵ < s}¨ s ← ⍵}
SUP 1 30 4 16 5 1 8

Output: 5 0 4 1 3 5 2
On a l’affectation s ← ⍵ de la liste vers s, puis pour chaque (noté par le ¨ ) élément ⍵, on compte +/ combien l’éléments sont plus grands que ⍵.
Avatar du membre
Schraf
Fonctionne à 1200 bauds
Fonctionne à 1200 bauds
Messages : 499
Enregistré le : 05 mars 2020 20:45
Contact :

Re: HP-48/49/50G : Quand le RPL rencontre APL

Message par Schraf »

APL ET RPL : CARACTÈRES

Dans cette vidéo sur APL, j’explique comment travailler sur chacun des éléments d’une liste. Nous avons vu que DOSUBS ou DOLIST permettent d’arriver au même résultat. Voyons sur cet exemple que cela fonctionne également avec des chaines de caractères.

Exemple 4 – R.A.T.P

Code : Tout sélectionner

⊃¨ 'RENTRE' 'AVEC' 'TES' 'PIEDS'
RATP
Ce qui signifie prendre le premier élément ⊃ de chaque terme du vecteur. Donc bien voir que :

Code : Tout sélectionner

⊃ 'RENTRE'
R
⊃ 'RENTRE' 'AVEC' 'TES' 'PIEDS'
RENTRE
En RPL, le premier élément d’une liste peut être récupéré par HEAD. Ce qui donne :

Code : Tout sélectionner

2: {"RENTRE" "AVEC" "TES" "PIEDS"}
1: « 1 « HEAD » DOLIST »
EVAL

1: {"R" "A" "T" "P"}
Pour avoir la concaténation et obtenir « RATP », sachez que le ∑LIST fonctionne aussi avec les listes de chaines de caractères…

Code : Tout sélectionner

2: {"RENTRE" "AVEC" "TES" "PIEDS"}
1: « 1 « HEAD » DOLIST ∑LIST »
EVAL

1: "RATP"
Exemple 5 – Brin d’ADN ?

Toujours tiré d’une de mes vidéos sur APL, on se demande si une chaine de caractères est un brin d’ADN, c’est-à-dire qu’il n’y a que les lettres A, C, T ou G. Le ET logique se note ∧ en APL.

Code : Tout sélectionner

ADN ← {∧/ ⍵ ∊ 'ACTG'}  ⍝ Réduction + appartenance
ADN 'ACCTTGCCA'
Output : 1          ⍝ C'est bien un brin ADN
ADN 'ACCTTXCCA'
Output : 0          ⍝ Ce n'est pas un brin ADN
Commençons par voir comment séparer les lettres d’une chaine de caractères en RPL. Une des solutions proposées par Norman Brenner est celle-ci :

Code : Tout sélectionner

« → s « « s j j SUB » 'j' 1 s SIZE 1 SEQ » »
'SP. STO
Ce qui signifie : on crée la suite (SEQ) pour j allant de 1 jusqu’à la taille de la chaine où chaque terme est égal à la portion de la chaine comprise entre j et j, donc le jième caractère.

Code : Tout sélectionner

1: "ACCTA"
VAR SP.

1: {"A" "C" "C" "T" "A"}
On doit maintenant parcourir chacun des caractères de la liste et regarder s’ils sont tous dans « ACTG ». Pour cela on va utiliser POS qui renvoie soit la position de la lettre dans la chaine soit zéro.

Dans un second temps on effectue une réduction par un ET sur l’ensemble des résultats, c’est-à-dire que l’on aura 0 si une des lettres n’était pas dans « ACTG ». Cela revient à faire le produit des éléments. Pour éviter d’avoir un nombre (produit des positions des lettres), on va regarder le signe du résultat. Ce qui donne :

Code : Tout sélectionner

« SP. 1 « "ACTG" SWAP POS » DOLIST ΠLIST SIGN »
'ADN STO

"ACCTTGCCA"
VAR ADN

1.           # C'est bien un brin ADN 

"ACCTTXCCA"
ADN

0.           # Ce n'est pas un brin ADN
Exemple 6 – Prix des sushis

Nouvel exemple de mes vidéos, sachant qu’un sushi coûte 2 EUR, combien coûte cette chaine : « S SS SSS S S » ?

Il faut compter le nombre de « S » et multiplier le résultat par 2.

Code : Tout sélectionner

SU ← {2 × +/ ⍵='S'}
SU '   S SS SSS S   S'
Output: 16
Vous ne devriez avoir aucun mal à lire cette version en RPL :

Code : Tout sélectionner

« SP. 1 « "S" == » DOSUBS ∑LIST 2 × » »
'SU STO
"   S SS SSS S   S"
VAR SU

16.
ou encore avec MAP :

Code : Tout sélectionner

« SP. « "S" == » MAP ∑LIST 2 × » »
'SU STO
"   S SS SSS S   S"
VAR SU

16.
Modifié en dernier par Schraf le 19 mai 2022 08:49, modifié 1 fois.
Avatar du membre
Schraf
Fonctionne à 1200 bauds
Fonctionne à 1200 bauds
Messages : 499
Enregistré le : 05 mars 2020 20:45
Contact :

Re: HP-48/49/50G : Quand le RPL rencontre APL

Message par Schraf »

APL ET RPL : PRODUIT INTERNE

L’exemple classique de produit interne est la fonction SOMMEPROD d’Excel permettant de faire une somme de produits. Ceci se traduit en APL par :

Code : Tout sélectionner

1 2 3 +.× 4 5 6
Output: 32            ⍝ (1×4) + (2×5) + (3×6)
Ce cas particulier s’obtient facilement en RPL :

Code : Tout sélectionner

2: {1 2 3}
1: {4 5 6}
×           # Produit terme à terme des 2 listes
∑LIST       # Somme des éléments 

1: 32
D’autres cas utiles en version APL :

Code : Tout sélectionner

∧.<             ⍝ Tous plus petits que...
∨.<             ⍝ Au moins un plus petit que...

4 6 8 ∧.< 10
1             ⍝ Tous plus petits que 10
14 16 8 ∨.< 10
1             ⍝ Au moins un plus petit que 10
Et en version RPL :

Code : Tout sélectionner

2: {4 6 8}
1: 10
< ΠLIST      # Produit de booléens = ET

1: 1         # Tous plus petits que 10

2: {4 6 8}
1: 10
< ∑LIST SIGN     # Signe somme booléens = OU

1: 1         # Au moins un plus petit que 10
Remarquez que l’on aurait pu utiliser une écriture plus proche de l’APL :

Code : Tout sélectionner

2: {4 6 8}
1: « 10 < « AND » STREAM »
EVAL
1: 1         # Tous plus petits que 10

2: {4 6 8}
1: « 10 < « OR » STREAM »
EVAL
1: 1         # Au moins un plus petit que 10
On en déduit une généralisation du produit interne « x f.g y », Norman Brenner propose la fonction IN. (notée IOPROD dans le document original)

Code : Tout sélectionner

« → f « EVAL f STREAM » »
'IN. STO
Exemple :

Code : Tout sélectionner

4: {1 2 3}
3: {4 5 6}
2: « × »
1: « + »
VAR IN.

1: 32
Le principe est assez simple, l’opérateur + est mis dans la variable f (et disparait de la pile), EVAL fait alors le produit entre les 2 listes puis on réduit (application de manière récursive avec STREAM) en utilisant f, ce qui signifie ici faire la somme des termes.

Exercice révision
Plus grande valeur commune entre 2 listes

Il s’agit de trouver quelle est la plus grande valeur commune entre 2 listes (ces listes pouvant comporter des nombres négatifs ou nuls). Voici le résultat à traduire en RPL :

Code : Tout sélectionner

MAXI ← {⌈/ (⍺ ∊ ⍵) / ⍺}
2 3 8 17 MAXI 3 5 7 8 9
Output: 8         ⍝ 8 est la plus grande valeur commune entre les 2 listes
Solution : Le symbole ⍺ désigne l’opérande à gauche et ⍵ l’opérande à droite. On crée le vecteur logique ⍺ ∊ ⍵ qui permettra de ne garder que les éléments de ⍺ qui appartiennent à ⍵. On réduit « / » ensuite ce vecteur par la fonction max ⌈. En utilisant la fonction CL. que l’on a construite dans la première partie et qui permet de faire une compression logique :

Code : Tout sélectionner

« 2 « IF NOT THEN DROP END » DOLIST »
'CL. STO
On peut définir MAXI par :

Code : Tout sélectionner

« → v « DUP
 1 « v SWAP POS SIGN » DOLIST »   # ⍺ ∊ ⍵ ?
 CL.                              # (⍺ ∊ ⍵) / ⍺
 « MAX » STREAM                   # ⌈/
»
'MAXI STO
Testons :

Code : Tout sélectionner

2: {2 3 8 1}
1: {3 5 7 8 9}
VAR MAXI

1: 8
Ou, si l’on ne veut pas utiliser CL. et n'avoir qu'une seule boucle :

Code : Tout sélectionner

« → v 
 « DUP          
  2 « IF v SWAP POS NOT   # Boucle (⍺ ∊ ⍵) / ⍺
      THEN DROP END
  » DOLIST
 »
 « MAX » STREAM           # ⌈/
»
'MAXI STO
Signalons que « MAX » STREAM ne fonctionne pas lorsqu’une liste est vide.

Code : Tout sélectionner

2: {2 3 8 1}
1: {4 4 4 4}
VAR MAXI

STREAM Error    # Car aucun élément en commun
Avatar du membre
Schraf
Fonctionne à 1200 bauds
Fonctionne à 1200 bauds
Messages : 499
Enregistré le : 05 mars 2020 20:45
Contact :

Re: HP-48/49/50G : Quand le RPL rencontre APL

Message par Schraf »

Extraction d’éléments

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

Code : Tout sélectionner

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 :

Code : Tout sélectionner

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 :

Code : Tout sélectionner

« → v « « v SWAP GET » MAP » »
'GL. STO
ou encore :

Code : Tout sélectionner

« → v « 1 « v SWAP GET » DOLIST » »
Vérifions :

Code : Tout sélectionner

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 :

Code : Tout sélectionner

« 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 :

Code : Tout sélectionner

« → s « « s j j SUB » 'j' 1 s SIZE 1 SEQ » »
'SP. STO
Vérifions que tout fonctionne :

Code : Tout sélectionner

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 :

Code : Tout sélectionner

'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.

Code : Tout sélectionner

« DUP TYPE 2 == « GLT. » « GL. » IFTE »
'GLG. STO
Vérifions :

Code : Tout sélectionner

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 ⍳) :

Code : Tout sélectionner

'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 :

Code : Tout sélectionner

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 :

Code : Tout sélectionner

« → 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 :

Code : Tout sélectionner

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.

Code : Tout sélectionner

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 :

Code : Tout sélectionner

« "AEIOU" POG. SIGN ∑LIST »
'VOY STO
En version « développée », on aurait :

Code : Tout sélectionner

« → 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 :

Code : Tout sélectionner

+/ 'TROPICALE' ∊ 'AEIOU'
Output : 4
Avatar du membre
Schraf
Fonctionne à 1200 bauds
Fonctionne à 1200 bauds
Messages : 499
Enregistré le : 05 mars 2020 20:45
Contact :

Re: HP-48/49/50G : Quand le RPL rencontre APL

Message par Schraf »

Exercice – Conversion en chiffres romains

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

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

Code : Tout sélectionner

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. vu précédemment :

Code : Tout sélectionner

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

1: {7. 6. 5. 5. 5. 4. 3. 2. 1.}
La récupération des valeurs par GLT. également vu précédemment :

Code : Tout sélectionner

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.

Code : Tout sélectionner

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 :

Code : Tout sélectionner

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

Code : Tout sélectionner

1: "MMMCMXCIX"
VAR ROM

1: 3999

1: "MMCCCXLV"
VAR ROM

1: 2345
Avatar du membre
Hobiecat
Fonctionne à 9600 bauds
Fonctionne à 9600 bauds
Messages : 3626
Enregistré le : 06 sept. 2011 14:57
Localisation : Normandie

Re: HP-48/49/50G : Quand le RPL rencontre APL

Message par Hobiecat »

Merci Schraf pour tous ces exemples !

L' APL est vraiment impressionnant de puissance...
FLISZT
Fonctionne à 1200 bauds
Fonctionne à 1200 bauds
Messages : 655
Enregistré le : 09 mars 2022 19:14

Re: HP-48/49/50G : Quand le RPL rencontre APL

Message par FLISZT »

Schraf a écrit : 14 mars 2022 09:15

Code : Tout sélectionner

« DUP DUP IP > NOT CL.»   
'ENT STO
La version a priori plus logique : IP == (Est-ce que le nombre est égal à sa partie entière) ne fonctionne pas car la comparaison ne s’effectue plus élément par élément mais entre les listes, le résultat renvoyé est 0 ou 1 et non plus un vecteur logique. Si quelqu’un a une idée...
Oui, utiliser « SAME » :

Code : Tout sélectionner

1: {1 2.5 3.7 8 4}
«       DUP DUP
	« IP » DOLIST
	2  « SAME » DOLIST
»
ce qui retourne :

Code : Tout sélectionner

2: {1 2.5 3.7 8 4}
1: { 1 0 0 1 1} 
puis avec le programme 'CL.' créé par Schraf

Code : Tout sélectionner

 « 2 « IF NOT THEN DROP END » DOLIST » 
on obtient :

Code : Tout sélectionner

1: {1 8 4}				@ soit la liste de départ ne contenant plus que des nombres entiers
Et bravo Schraf pour ce fil déjà passionnant avant même d'être terminé ! :)
Bruno
Sanyo CZ-0124 ? TI-57 ? HP-15C ? Canon X-07 + XP-140 Monitor Card ? HP-41CX ? HP-28S ? HP-50G ? HP-50G
Avatar du membre
Schraf
Fonctionne à 1200 bauds
Fonctionne à 1200 bauds
Messages : 499
Enregistré le : 05 mars 2020 20:45
Contact :

Re: HP-48/49/50G : Quand le RPL rencontre APL

Message par Schraf »

Exercice – Union

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

Code : Tout sélectionner

∪ 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 :

Code : Tout sélectionner

{⍺ ∊ ⍵: ⍵ ⋄ ⍺,⍵} / 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) :

Code : Tout sélectionner

« {{}} 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 :

Code : Tout sélectionner

{⍺ ∊ ⍵: ⍵ ⋄ ⍺,⍵} / 4 5 4
5 4
Alors qu’en RPL :

Code : Tout sélectionner

{ 4 5 4 }
VAR UN.
{ 4 5 }
Avatar du membre
Schraf
Fonctionne à 1200 bauds
Fonctionne à 1200 bauds
Messages : 499
Enregistré le : 05 mars 2020 20:45
Contact :

Re: HP-48/49/50G : Quand le RPL rencontre APL

Message par Schraf »

APL ET RPL : SCAN

En APL il existe l’opérateur \ (appelé scan ou balayage ou réduction partielle) qui fait penser à l’opérateur / de réduction. Voici la différence :

Code : Tout sélectionner

f/ 1 2 3 … = 1 f 2 f 3 … (en partant de la droite vers la gauche pour les calculs)
f\ 1 2 3 … = (f/1) (f/1 2) (f/1 2 3) …
Le n-ieme résultat est égal à la réduction des n premiers éléments. L’exemple de base est la somme cumulée des éléments d’un vecteur :

Code : Tout sélectionner

+\ 2 3 4 8 10
2 5 9 17 27
On peut imaginer une version en RPL utilisant SEQ pour créer ces suites ou rester avec notre trio DOLIST, DOSUBS et STREAM. En effet, si on reprend la réduction :

Code : Tout sélectionner

1: { 2 3 4 8 10 }
« + »
PRG LIST PROC STREAM
1: 27
Mais si l’on ajoute OVER pour dupliquer à chaque fois le résultat précédent :

Code : Tout sélectionner

1: { 2 3 4 8 10 }
« OVER + »
PRG LIST PROC STREAM
5: 2
4: 5
3: 9
2: 17
1: 27
On obtient les bonnes valeurs mais elles seront sur la pile. Pour comprendre le principe, tapez sur votre machine :

2 ENTER 3 OVER + 4 OVER + 8 OVER + 10 OVER +

Reste à la transformer en liste { 2 5 9 17 27}. On peut imaginer cette version pour le cas d’une somme cumulée :

Code : Tout sélectionner

« → v
 « v « OVER + » STREAM v SIZE →LIST »
»
'SCUM STO

1: { 2 5 3 4}
VAR SCUM
1: {2 7 10 14}
Ou, de façon plus générale, définir SCA. (Scan, appelé PRED dans le document d’origine de Norman Brenner) :

Code : Tout sélectionner

« → v f
 « v « OVER f EVAL » STREAM v SIZE →LIST »
»
'SCA. STO
Exemples :

Code : Tout sélectionner

1: {1 2 3 4}
2: « + »
VAR SCA.
1: {1 3 6 10}

1: {2 5 4 1 8 3 3}
2: « MAX »
VAR SCA.
1: {2 5 5 5 8 8 8}  # Maximum progressif
En APL :

Code : Tout sélectionner

⌈\ 2 5 4 1 8 3 3
2 5 5 5 8 8 8
En particulier, lorsque les vecteurs sont binaires, cela permet d’avoir des résultats intéressants :

Code : Tout sélectionner

∧\ 1 1 1 0 1 0 0 1
   1 1 1 0 0 0 0 0    ⍝ = 0 à partir du 1er "0"
∨\ 0 0 0 0 1 0 0 1
   0 0 0 0 1 1 1 1    ⍝ = 1 à partir du 1er "1"
<\ 0 0 1 0 1 1 0 0 1
   0 0 1 0 0 0 0 0 0  ⍝ Position du 1er "1"
En RPL :

Code : Tout sélectionner

2: {1 1 1 0 1 0 0 1}
1: « AND »
VAR SCA.
1: {1 1 1 0 0 0 0 0}

2: {0 0 0 0 1 0 0 1}
1: « OR »
VAR SCA.
1: {0 0 0 0 1 1 1 1}
Lorsque l’opérateur f est commutatif (comme OR, AND, +, MAX…) il n’y a pas de différence entre le RPL et l’APL. Par contre, pour des opérateurs non commutatifs comme « < », le résultat sera faux.

APL : f\ a b c = (a) (a f b) (a f (b f c))

RPL : f\ a b c = (a) (a f b) ((a f b) f c)

Pour se convaincre :

Code : Tout sélectionner

2: {0 0 1 0 1 1 0 0 1}
1: « < »
VAR SCA.
1: {0 0 0 0 0 0 0 0 0}

Résultat attendu : 0 0 1 0 0 0 0 0 0
Exemple – Les fractions continues

Pour la définition d’une fraction continue, voir par exemple Wikipedia.

En APL, on obtient la valeur approchée d’une liste d’étages par :

Code : Tout sélectionner

+∘÷/ 1 2 2 2 2
1.413793103
Qui correspond à la valeur 1 + 1 / ( 2 + 1 / ( 2 + 1 / ( 2 + 1 / 2 ) ) )

Et en utilisant un scan :

Code : Tout sélectionner

+∘÷\ 1 2 2 2 2
1 1.5 1.4 1.416666667 1.413793103
On obtient les fractions 1, puis 1 + 1 / 2 puis 1 + 1 / (2 + 1 / 2)…

En RPL, on peut obtenir la valeur finale par :

Code : Tout sélectionner

« REVLIST « OVER INV + » STREAM →NUM »
'FC STO

1: { 1 2 2 2 2 }
VAR FC
1: 1.41379310345     # ≃ racine de 2

1: { 1 1 1 1 1 1 1 1 1 1 }
VAR FC
1: 1.61818181818     # ≃ nombre d'or
L’opération « Prendre l’inverse plus additionner » n’étant pas commutative, on ne pourra pas utiliser notre fonction SCA.
Avatar du membre
Schraf
Fonctionne à 1200 bauds
Fonctionne à 1200 bauds
Messages : 499
Enregistré le : 05 mars 2020 20:45
Contact :

Re: HP-48/49/50G : Quand le RPL rencontre APL

Message par Schraf »

Exercice - Les immeubles

On vous donne une liste de hauteurs d'immeubles adjacents et on vous demande combien seront visibles si vous les regardez à partir de la gauche. Par exemple, si les hauteurs sont 2 3 5 2 1 6 4, en vert ci-dessous les 4 seuls immeubles qui seront visibles (les autres sont cachés par des bâtiments plus hauts)

Les immeubles
Les immeubles
imm1.jpg (11.74 Kio) Vu 5635 fois
On supposera dans un premier temps qu'il n'y a pas de zones vides entre les immeubles, c'est-à-dire que la liste ne contient pas de 0 (immeubles de hauteur nulle).

Code : Tout sélectionner

IMM 2 3 5 2 1 6 4
Output: 4
Dans un second temps, considérez le cas général.

Corrigé version 1 : Commençons par l'APL. On va déjà scanner les immeubles pour récupérer les hauteurs maximales atteintes :

Code : Tout sélectionner

       ⌈\ 2 3 5 2 1 6 4
Output:   2 3 5 5 5 6 6
Il faut maintenant récupérer les hauteurs distinctes :

Code : Tout sélectionner

       ∪ ⌈\ 2 3 5 2 1 6 4
Output:     2 3 5 6
Et les compter :

Code : Tout sélectionner

⍴ ∪ ⌈\ 2 3 5 2 1 6 4
Output: 4
Le programme final en APL pour la version 1 :

Code : Tout sélectionner

IMM ← {⍴∪⌈\⍵}
Nous pouvons immédiatement traduire cela en RPL avec les programmes SCA. (scan), UN. (union) et la fonction SIZE :

Code : Tout sélectionner

« « MAX » SCA. UN. SIZE »
'IMM STO
Vérifions :

Code : Tout sélectionner

1: { 2 3 5 2 1 6 4 }
VAR IMM
1: 4.
Corrigé de la version générale : Si la liste commence par un ou plusieurs 0, le calcul sera faux :

Code : Tout sélectionner

1: { 0 2 1 }
VAR IMM
1: 2.
Ceci parce que les maximums progressifs sont { 0 2 2 } dont la réunion comporte 2 termes {0 2}. Il faut donc filtrer la liste des maximums pour enlever les 0. Une solution possible en APL :

Code : Tout sélectionner

IMM ← {⍴∪ (0 ≠ n) / n ← ⌈\⍵}			⍝ ou encore IMM ← {0 +.≠ ∪ ⌈\ ⍵}
Et nous avions créer en RPL la fonction CL. pour compresser une liste en utilisant un vecteur logique. D'où cette version en RPL :

Code : Tout sélectionner

« « MAX » SCA. DUP 0 > CL. UN. SIZE »
'IMM STO
Qui signifie prendre les maximums progressifs, dupliquer cette liste, créer le vecteur logique des termes > 0, éliminer les éléments qui sont à FAUX, faire la réunion des éléments restants et les compter.

Code : Tout sélectionner

1: { 0 2 1 }
VAR IMM
1: 1.
Modifié en dernier par Schraf le 10 août 2022 14:34, modifié 1 fois.
Avatar du membre
C.Ret
Fonctionne à 9600 bauds
Fonctionne à 9600 bauds
Messages : 3404
Enregistré le : 31 mai 2008 23:43
Localisation : N 49°22 E 6°10

Re: HP-48/49/50G : Quand le RPL rencontre APL

Message par C.Ret »

Schraf a écrit : 18 mars 2022 08:31 Exercice - Les immeubles

On vous donne une liste de hauteurs d'immeubles adjacents et on vous demande combien seront visibles si vous les regardez à partir de la gauche. Par exemple, si les hauteurs sont 2 3 5 2 1 6 4, en vert ci-dessous les 4 seuls immeubles qui seront visibles (les autres sont cachés par des bâtiments plus hauts)
Troll Eric Scharf.jpg
Troll Eric Scharf.jpg (10.95 Kio) Vu 5586 fois
On supposera dans un premier temps qu'il n'y a pas de zones vides entre les immeubles, c'est-à-dire que la liste ne contient pas de 0 (immeubles de hauteur nulle).
J'aime bien ton exercice, mais j'y comprends rien: vu où tu places l'œil (unique?) du sujet, il faut que tu retires la grosse flèche noire, car sinon, je pense qu'il ne voit que deux immeubles; Et encore, je crois qu'il n'entrevoie qu'une toute petite partie du second ! :D

OK, ok ne nous fâchons pas, je sort immédiatement :arrow:
Modifié en dernier par C.Ret le 12 mai 2022 19:55, modifié 1 fois.
SHARP PC-1211 PC-1360 EL-5150 PC-E500 | Commodore C=128D | Texas Instruments Ti-57LCD Ti-74BASICalc Ti-92II Ti-58c Ti-95PROCalc Ti-30XPROMathPrint | Hewlett-Packard HP-28S HP-41C HP-15C HP-Prime HP-71B | CASIO fx-602p | NUMWORKS | Graphoplex Rietz Neperlog | PockEmul | Sommaire des M.P.O. | Ma...dov'il sapone.
Avatar du membre
Schraf
Fonctionne à 1200 bauds
Fonctionne à 1200 bauds
Messages : 499
Enregistré le : 05 mars 2020 20:45
Contact :

Re: HP-48/49/50G : Quand le RPL rencontre APL

Message par Schraf »

@C.Ret : Même si je sais que tu as parfaitement compris ce que je voulais dire en regardant le programme final, on peut imaginer un petit OVNI qui se déplace de haut en bas du côté gauche des immeubles et qui peut scanner horizontalement autour de lui. Quels sont les immeubles qui seront touchés par ce scanner ?

Scanner horizontal
Scanner horizontal
ovni.jpg (25.68 Kio) Vu 5552 fois
Avatar du membre
C.Ret
Fonctionne à 9600 bauds
Fonctionne à 9600 bauds
Messages : 3404
Enregistré le : 31 mai 2008 23:43
Localisation : N 49°22 E 6°10

Re: HP-48/49/50G : Quand le RPL rencontre APL

Message par C.Ret »

En réalité, je suis avec beaucoup d'intérêt tes exposés, en particulier, je cherche à adapter à mes machines.
Mais surtout, je cherche à entretenir mes vagues et maigres connaissances en APL

Ce qui donne des choses lentes et poussives, n'ayant alors rien d'intelligent à ajouter, je m'amuse à troller.

P.S.: J'aime bien les soucoupes qui tirent au rayon laser cela ma rappelle quelque jeu ancien. J'ai le nom sur le bout de la langue... retrouve plus :(




Image
Modifié en dernier par C.Ret le 12 mai 2022 19:55, modifié 1 fois.
SHARP PC-1211 PC-1360 EL-5150 PC-E500 | Commodore C=128D | Texas Instruments Ti-57LCD Ti-74BASICalc Ti-92II Ti-58c Ti-95PROCalc Ti-30XPROMathPrint | Hewlett-Packard HP-28S HP-41C HP-15C HP-Prime HP-71B | CASIO fx-602p | NUMWORKS | Graphoplex Rietz Neperlog | PockEmul | Sommaire des M.P.O. | Ma...dov'il sapone.
FLISZT
Fonctionne à 1200 bauds
Fonctionne à 1200 bauds
Messages : 655
Enregistré le : 09 mars 2022 19:14

Re: HP-48/49/50G : Quand le RPL rencontre APL

Message par FLISZT »

C.Ret a écrit : 19 mars 2022 13:43 P.S.: J'aime bien les soucoupes qui tirent au rayon laser cela ma rappelle quelque jeu ancien. J'ai le nom sur le bout de la langue... retrouve plus :(
Space Invaders, peut-être… (?) :geek:
Bruno
Sanyo CZ-0124 ? TI-57 ? HP-15C ? Canon X-07 + XP-140 Monitor Card ? HP-41CX ? HP-28S ? HP-50G ? HP-50G
Répondre

Retourner vers « Tous les Pockets »