Background
L'opération de collecte de Scala est cool, n'est-ce pas? Vous pouvez écrire proprement sans créer de variables intermédiaires supplémentaires.
Scala
val result = (0 to 10000)
.filter(_ % 3 == 0)
.map(_ + 1)
.groupBy(_ % 10)
.map { it =>
val k = it._1
val v = it._2.sum
(k, v)
}.toList
Ce code est la somme de chaque nombre lorsque les nombres de 0 à 10000 sont laissés sous forme de multiple de 3 et divisés par 1 et divisés en groupes. Ce calcul n'a pas de signification particulière, mais c'est un exemple dans lequel le flux de traitement des données peut être écrit d'une manière très facile à comprendre (cool) avec exactement le même ordre de pensée.
Quand j'essaye de faire ça en Python ...
Python
import itertools
result = range(0, 10001)
result = filter(lambda x: x % 3 == 0, result)
result = map(lambda x: x + 1, result)
result = map(lambda x: (x % 10, x), result)
result = sorted(result)
result = itertools.groupby(result, lambda x: x[0])
result = map(lambda x: (x[0], sum(map(lambda _: _[1], x[1]))), result)
result = list(result)
C'est difficile à voir et je ne peux même pas le voir. Au fait, si vous écrivez d'un seul coup sans utiliser de variables intermédiaires
Python
result = list(
map(lambda x: (x[0], sum(map(lambda _: _[1], x[1]))),
itertools.groupby(
sorted(
map(lambda x: (x % 10, x),
map(lambda x: x + 1,
filter(lambda x: x % 3 == 0,
range(0, 100001)
)
)
), lambda x: x[0]
)
)
)
)
Le code avec une lisibilité 0 est terminé. Vous pouvez vous réveiller à quelque chose lorsque vous pouvez lire ceci en douceur. La raison en est que si vous voulez traiter dans l'ordre f-> g-> h, vous devez écrire dans l'ordre inverse comme h (g (f (x))).
En fait, il existe une bibliothèque qui résout ce problème. Oui, avec toolz
, scalafunctional
et fn.py
. Dans cet article, des opinions telles que ** Ecrire en Scala ** sont des mots NG.
Toolz, CyToolz
toolz
est une bibliothèque qui étend les ʻitertools et functools
intégrés à Python afin qu'ils puissent être écrits de manière plus fonctionnelle. cytoolz
est également une version plus rapide de celui-ci recréée en Cython. Les fonctions «pipe» et curled implémentées dans ces derniers sont très utiles. Le terrible code ci-dessus peut être écrit comme suit:
from cytoolz.curried import *
import operator as O
result = pipe(range(0, 10001),
filter(lambda x: x % 3 == 0),
map(lambda x: x + 1),
reduceby(lambda x: x % 10, O.add),
lambda d: d.items(),
list
)
Comment c'est? Donnez les données que vous souhaitez traiter avec le premier argument et indiquez les fonctions que vous souhaitez appliquer avec le deuxième argument et les suivants les uns après les autres. Si vous êtes familier avec le langage R, vous pouvez penser à dplyr
. Au fait, le filtre
, map
et reductionby
utilisés ici sont tous curry, donc map (f, data) ʻest multipliée comme
map (f) (data). Vous pouvez vous connecter avec
pipecomme ceci. Si vous n'utilisez pas la version recourbée, remplacez
pipe par
thread_last` et les données traitées par la fonction précédente seront passées au dernier argument de chaque fonction l'une après l'autre.
ScalaFunctional
Comme son nom l'indique, scalafunctional
est une bibliothèque qui vous permet d'exploiter la collection Scala comme. ~~ Si vous voulez aller aussi loin, ajoutez Scala (ry ~~ Dans cette bibliothèque, mettez list
, dict
, etc. dans une classe dédiée appelée seq
et traitez-la dans une chaîne de points.
from functional import seq
result = seq(range(0, 10001)) \
.filter(lambda x: x % 3 == 0) \
.map(lambda x: x + 1) \
.map(lambda x: (x % 10, x)) \
.reduce_by_key(O.add) \
.to_list()
C'est le plus proche de Scala. Cependant, Python nécessite une barre oblique inverse à la fin de la ligne, ce qui est un peu ennuyeux. Après cela, l'expression «lambda» de Python n'est pas aussi flexible que la fonction de Scala, il peut donc être nécessaire de «déf» la fonction une fois pour un traitement compliqué. Quoi qu'il en soit, c'est très simple et beau.
fn.py
fn.py
est également une bibliothèque pour la programmation fonctionnelle Python. La plus grande caractéristique est qu'il peut être écrit comme un espace réservé Scala.
from fn import _
result = map(_ + 1, range(10))
Vous pouvez simplement l'utiliser à la place de lambda
.
f = _ + 1
f(10)
>>>
11
Cela va bien avec toolz
et scalafunctional
.
toolz
result = pipe(range(10),
map(_ + 1),
list
)
scalafunctional
result = seq(range(10)) \
.map(_ + 1) \
.to_list()
Au fait, dans IPython etc., _
semble être après réservation pour représenter la dernière sortie, donc lors de son utilisation là-bas, il est nécessaire de ʻimport` avec un autre nom.
from fn import _ as it
Si vous utilisez toolz
et scalafunctional
, la programmation fonctionnelle sera améliorée même en Python. scalafunctional
peut être écrit exactement comme l'opération de collecte de Scala. D'autre part, vous pouvez utiliser pipe
of toolz
pour écrire non seulement des opérations de collecte, mais aussi un flux de traitement de données plus polyvalent. Veuillez bien les combiner avec fn.py
et profiter de la vie fonctionnelle de Python [^ pandas].
Toutes les bibliothèques utilisées cette fois sont publiées sur GitHub. Bien sûr, il est également enregistré dans PyPI, vous pouvez donc l'installer avec pip
.
[^ pandas]: Même les Pandas standard peuvent être connectés à DataFrame par chaîne de points dans une certaine mesure.
Recommended Posts