Douzième exercice en Python, JavaScript et APL

Résumé en français : On vous donne une chaine de caractères composée de “chiffres” (‘0’ à ‘9’). Vous devez écrire une fonction qui renvoie une chaine où chaque chiffre est répété le nombre de fois correspondant à sa valeur. Par exemple avec la chaine “312”, on doit répéter 3 fois le “3”, 1 fois le “1” et 2 fois le “2”, ce qui donne la chaine “333122”.

Version classique

Première idée, utiliser 2 boucles. La première pour récupérer un à un les caractères de la chaine et la seconde pour dupliquer le bon nombre de fois chacun de ces caractères.

python

def explose(s):
  sortie = ''                      # initialisation du résultat final
  for c in s:                      # on parcourt la chaine
    for n in range(int(c)):        # on ajoute le bon nombre de fois...
      sortie += c                  # ...le caractère
  return sortie                    # retour du résultat

>>  explose("312")
'333122'
>> explose("302")
'33322'
>> explose("102269")
'12222666666999999999'

Une seule boucle + répéter

En Python, JavaScript ou APL, il est simple de répéter un caractère :

Python

>> 'a' * 5
'aaaaa'

JavaScript

>> 'a'.repeat(5)
'aaaaa'

APL

       5 ⍴ 'a'
aaaaa

On peut également répéter un caractère 0 fois, dans ce cas on obtient la chaine vide. D’où cette seconde version :

Python

def explose(s):
  sortie = ''
  for c in s:
    sortie += c * int(c)        # on répète le caractère
  return sortie

JavaScript

const explose = s => {
    sortie = '';
    for (c of s) sortie += c.repeat(+c);     // Voir dessous pour +c
    return sortie
}

>> + '5'             // Transformer une chaine en nombre
5

>> Number('5')       // Même chose que Number
5

Autres écritures : join, map, reduce

Nous devons transformer (map) chaque caractère en sa répétition, ce qui donne un tableau de taille celle de la chaine initiale :

Python

>> [c * int(c) for c in "312"]         # Transformer 3 "chiffres"
['333', '1', '22']                     # Tableau à 3 éléments

JavaScript

>> [..."312"].map(c => c.repeat(+c))   // map = transformation
['333', '1', '22']

APL

     3 1 2 ⍴¨ '312'      ⍝ le ¨ signifie "pour chaque"
┌───┬─┬──┐
│333│1│22│
└───┴─┴──┘

Il suffit ensuite de joindre les différents éléments, d’où cette troisième version :

Python

def explose(s):
  return ''.join(c * int(c) for c in s)

>> explose("44012")
'44444444122'

JavaScript v1

const explose = s => [...s].map(c => c.repeat(+c)).join('')

>> explose('55011')
'555555555511'

JavaScript est tolérant sur les mélanges de types :

>> 'a'.repeat('3')     // utilisation de '3' au lieu de 3
'aaa'
>> 3 * '4'             // multiplication d'un nombre par un caractère
12

JavaScript v2

>> const explose = s => [...s].map(c => c.repeat(c)).join``

On peut également utiliser reduce, c’est-à-dire partir d’une chaine vide et au fur et à mesure ajouter les caractères répétés, voici une version en JavaScript :

const explose = s => [...s].reduce((a, c) => a + c.repeat(c), '')

>> explose("44012")
'44444444122'

APL

Nous avons déjà vu comment transformer une chaine en vecteur :

      s ← '312'

      ⍎¨s          ⍝ On obtient un vecteur de 3 caractères
3 1 2

Remarquons que nous devons dupliquer les caractères et ensuite les concaténer :

      (3 ⍴ '3') (1 ⍴ '1') (2 ⍴ '2')      ⍝ On duplique les caractères  
┌───┬─┬──┐
│333│1│22│
└───┴─┴──┘
     ,/ (3 ⍴ '3') (1 ⍴ '1') (2 ⍴ '2')    ⍝ Concaténation
┌──────┐
│333122│
└──────┘

C’est exactement ce que fait un produit interne f.g, à savoir :

x1 x2 x3 f.g y1 y2 y3 signifie réduction f/ appliquée à (x1 g y1) (x2 g y2) (x3 g y3)

      3 1 2 ,.⍴ '312'       ⍝ On duplique puis concatène
┌──────┐
│333122│
└──────┘

      {(⍎¨⍵) ,.⍴ ⍵} '312'   ⍝ Créons notre fonction
┌──────┐
│333122│
└──────┘

      (⍎¨,.⍴⊢) '31402'      ⍝ Même version sans utiliser ⍵
┌──────────┐
│3331444422│
└──────────┘

⍝ Version finale en APL

      explose ← ⍎¨ ,.⍴ ⊢

      explose '314159'
┌───────────────────────┐
│33314444155555999999999│
└───────────────────────┘

      explose ← ∊ ⍎¨ ,.⍴ ⊢

      explose '314159'
33314444155555999999999

Expressions régulières

Une autre idée est de ce dire que chaque “chiffre” doit être remplacé par sa duplication. Voyons comment on effectue des remplacements en Python et JavaScript :

javascript

>> 'bonjour'.replace('o','*')     // Un seul 'o' sera remplacé par '*'
'b*njour'

>> 'bonjour'.replace(/o/g,'*')    // Tous les 'o' sont remplacés
'b*nj*ur'                         // 'g' pour global

>> "3a1b22".replace(/\d/g, '*')   // Remplacer les chiffres (digits)
'*a*b**'

>> '4032'.replace(/./g, v => 9 - v)  // '.' = caractère quelconque
'5967'       // Les chiffres sont remplacés par 9 - valeur

// Mettre toutes les voyelles en majuscules

>> "okjaicompris".replace(/a|e|i|o|u/g, c => c.toUpperCase())
'OkjAIcOmprIs'

D’où cette version finale en JavaScript :

const explose = s => s.replace(/./g, v => v.repeat(v))

python

Python a la méthode replace pour des remplacements simples.

>> 'bonjour'.replace('o','*')     # Tous les 'o' sont remplacés
'b*nj*ur'

D’où l’idée de remplacer chacun des caractères de ‘0’ à ‘9’ par leur duplication :

def explose(s):
    for i in range(10):
        s = s.replace(str(i), str(i) * i)
    return s

>> explose('314159')
'33314444155555999999999'

Pour utiliser des expressions régulières (Regex), nous devons importer la bibliothèque re.

>> import re
>> re.sub(r'\d','*','3a1b22')     # Remplacer les chiffres par '*'
'*a*b**'

On peut également effectuer des transformations, pour cela on :
recherche les éléments à modifier à l’aide d’une expression régulière
récupère la chaine correspondante (group ou [0])
– effectue la transformation (lambda x : …)

# Mettre toutes les voyelles en majuscules

>> re.sub(r'a|e|i|o|u', lambda x: x.group().upper(), 'okjaicompris')
'OkjAIcOmprIs'

# Ecriture équivalente en utilisant [0]

>> re.sub(r'a|e|i|o|u', lambda x: x[0].upper(), 'okjaicompris')
'OkjAIcOmprIs'

# Transformer chaque chiffre en 9 - chiffre :

>> re.sub(r'.',lambda x: str(9 - int(x[0])), '4032')
'5967'

Ce qui nous donne cette version finale en Python :

import re

def explose(s):
  return re.sub(r'.',lambda v: v[0] * int(v[0]), s)