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)