PHP:
implode(" ", array("Fulguro", "Poing")) |
implode(" ", array("Fulguro", "Poing"))
Perl:
join(" ", qw(Astero Hache)) |
join(" ", qw(Astero Hache))
Ruby:
%w(Planitron Cornofulgur).join(' ') |
%w(Planitron Cornofulgur).join(' ')
Et…
Python:
' '.join(['pouet', 'pouet']) |
' '.join(['pouet', 'pouet'])
C'est un des rares rage comics qui me fasse marrer
Mais pourquoi, mon Dieu, pourquoi ?
A cause du duck typing: on se branle du type et de sa capacité à joiner, on est juste intéressé par le fait qu’il soit itérable.
Ainsi, join()
accepte n’importe quel itérable:
>>> '-'.join('azerty') # une string
'a-z-e-r-t-y'
>>> '-'.join(['a', 'b', 'c']) # une liste/un dico/un tuple/un set
'a-b-c' |
>>> '-'.join('azerty') # une string
'a-z-e-r-t-y'
>>> '-'.join(['a', 'b', 'c']) # une liste/un dico/un tuple/un set
'a-b-c'
>>> '-'.join((str(x ** x) for x in range(10))) # un expression génératrice
'1-1-4-27-256-3125-46656-823543-16777216-387420489' |
>>> '-'.join((str(x ** x) for x in range(10))) # un expression génératrice
'1-1-4-27-256-3125-46656-823543-16777216-387420489'
>>> '-'.join(open('/etc/fstab'))[:100] # un fichier (et tout file like object)
"# /etc/fstab: static file system information.\n-#\n-# Use 'blkid -o value -s UUID' to print the univer" |
>>> '-'.join(open('/etc/fstab'))[:100] # un fichier (et tout file like object)
"# /etc/fstab: static file system information.\n-#\n-# Use 'blkid -o value -s UUID' to print the univer"
>>> def generator(l): # et n'importe quel générateur custo
count = 1
for x in l:
yield x * count
count = count * (count + 1)
...
>>> '-'.join(generator('pouet'))[:100]
'p-oo-uuuuuu-eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-ttttttttttttttttttttttttttttttttttttttttttttt' |
>>> def generator(l): # et n'importe quel générateur custo
count = 1
for x in l:
yield x * count
count = count * (count + 1)
...
>>> '-'.join(generator('pouet'))[:100]
'p-oo-uuuuuu-eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-ttttttttttttttttttttttttttttttttttttttttttttt'
>>> class LaClasse(object): # VRAIMENT n'importe quel itérable
def __iter__(self):
import datetime
for x in xrange(10):
yield datetime.datetime.now().strftime('%f')
...
>>> '-'.join(LaClasse())
'902564-902628-902641-902650-902659-902667-902674-902682-902692-902710' |
>>> class LaClasse(object): # VRAIMENT n'importe quel itérable
def __iter__(self):
import datetime
for x in xrange(10):
yield datetime.datetime.now().strftime('%f')
...
>>> '-'.join(LaClasse())
'902564-902628-902641-902650-902659-902667-902674-902682-902692-902710'
>>> # et même une chaîne batarde d'itérables improbables :-)
>>> list(iter(chain(LaClasse(), (str(x) for x in xrange(10))).next, '5'))
['043498', '043546', '043557', '043567', '043575', '043584', '043592', '043600', '043608', '043616', '0', '1', '2', '3', '4'] |
>>> # et même une chaîne batarde d'itérables improbables :-)
>>> list(iter(chain(LaClasse(), (str(x) for x in xrange(10))).next, '5'))
['043498', '043546', '043557', '043567', '043575', '043584', '043592', '043600', '043608', '043616', '0', '1', '2', '3', '4']
Et accessoirement ça permet de faire des trucs comme ça:
>>> join_coma = ",".join
>>> join_dash = "-".join
>>> join_coma('pouet')
'p,o,u,e,t'
>>> join_dash('pouet')
'p-o-u-e-t' |
>>> join_coma = ",".join
>>> join_dash = "-".join
>>> join_coma('pouet')
'p,o,u,e,t'
>>> join_dash('pouet')
'p-o-u-e-t'
ça fait un peu réfléchir à l’envers mais on s’y fait… Et encore c’est parce que les codeurs pensent de travers :
‘,’.join(ma_liste) => ‘,’ joint les éléments de ma liste
Ce que propose Ruby roxe quand même pour ce qui est de la lisibilité, surtout associé à des blocs.
Mais ‘implode’ en PHP ? pourquoi ?
Parceque c’est l’inverse d’explode :-)
Parce que join = implode :
http://in.php.net/manual/en/function.join.php
et pourquoi on a:
filter(function, sequence)
etmap(function, sequence)
et pas:
sequence.filter(function)
etsequence.map(function)
car lors de l’imbrication de plusieurs
map
etfilter
la seconde solution est nettement plus lisible (pas besoin de jouer au poupées russes)Pour la même raison: pour qu’on puisse passer n’importe quel itérable à map et filter. On peut faire un map/filter sur autre chose qu’un séquence. Un itérable doit juste remplir une seule condition: pouvoir récupérer des valeurs une à une.
Si on suit la logique sequence.filter, sequence.map, sequence.join, alors on se retrouve à demander à tous les itérable d’implémenter un proxy de filter, map et join, ce qui revient à complexifier l’interface de chaque nouvel itérable codé.
Python fonctionne en duck typing, si c’est itérable, on peut maper, on peut filtrer, on peut joiner. L’action de maper, filtrer, joiner est indépendance de l’itérable, et donc ne fait pas partie de son interface.
Pourquoi map(func, iterable), merci pour l’explication. Je comprends mieux maintenant.
J’ai un ami qui il y a longtemps m’avait expliqué que c’etait (entre autre?) pour encourager un style de programmation purement fonctionnel.
OK implode est l’inverse d’explode mais PHP aurait pu coller à Perl et au reste du monde et utiliser split / join !
Pour les histoires de map / filter / reduce : c’est vrai que des fois, on a l’impression que Python ne sait pas trop choisir entre fonction et méthodes… Dans ce cadre là, c’est le choix de rendre l’objet iterable grâce à la méthode _iter_ et pas en rajoutant une tonne de méthodes comme avec un mixin comme dans Ruby (parce que Ruby ne rigole pas avec la taille des interfaces, c’est bien chiant d’ailleurs)
Voilà. Moi autant
','.join
me parle complètement, autantlen(obj)
à la place deobj.length
je trouve ça bidon puisquelen()
ne fait qu’appeler __len__ de toute façon. Encore, silen()
pouvait fallback sur__iter__
, mais même pas.