j'ai un petit soucis avec Ruby. Je travaille sur des jeux de données relativement volumineux. Je les stocke dans des tables de hachage pour un traitement rapide (j'ai de la RAM tant que je veux) et j'y applique quelques fonctions.
Par exemple, j'ai une fonction qui me calcule la mesure de Jaccard entre deux vecteurs. On se fout pas mal de l'implémentation (qui consiste à comparer les éléments identiques et disjoints entre les deux vecteurs: plus il y a d'élément identiques, plus la mesure est élevé, plus il y a d'élément différent, plus la mesure est faible, tout ça entre 0 et 1).
Or, quand j'appelle ma fonction, je fais
jaccard(vec_1, vec_2)
Ce qui prend pas mal de temps, parce que j'ai l'impression (et google confirme, irb aussi) qu'il recopie vec_1 et vec_2 à l'appel de la fonction, que je fais ce calcul environ 180 000 fois (j'ai beaucoup d'éléments à comparer) et que chaque vecteur est de dimension environ 3000. Du coup, la copie prend un peu de temps et plombe pas mal le calcul, déjà bien long (3000 * 180 000).
Je cherche donc une façon élégante d'appeler les /références/ à vec_1 et vec_2, puisque de toutes façons je ne modifie pas ces objets dans la fonction.
Prenons un cas plus simple. J'ai un tableau a = ["a","b","c"] et une fonction bete :
def rend_x (vecteur)
vecteur = ["x", "x", "x"]
end
Je voudrais appeler rend_x(a), et que a soit transformé en ["x", "x", "x"] (c'est-à-dire donc, que ma fonction modifie directement l'élément a -- j'insiste: c'est un exemple jouet, en pratique je m'en fout complètement, je veux juste qu'il ne recopie pas /a/ à l'appel de la fonction).
En perl c'est facile (et pour le coup, je réimplémente en ruby un script perl que je trouve bien trop gruik, mais si mon script ruby est beaucoup plus lent -- il l'est -- je vais avoir du mal à le vendre, meme s'il est beaucoup plus propre).
# Itération
Posté par Duncan Idaho . Évalué à 2.
J'ai une table de hachage qui contient des tables de hachage. J'itère dessus :
# J'ai quelque part un vecteur vec_1
result = Hash.new
# Je veux comparer vec_1 avec tous les autres vecteurs (vec_2) qui sont stockés dans
# la table de hachage "hachage".
hachage.each do |cle, valeur| # valeur est une table de hachage
valeur.each do |osef, vec_2|
result[cle] = jaccard(vec_1, vec_2)
end
end
Dans ce cas, non seulement je voudrais appeler les références à vec_1 et vec_2, mais je voudrais également que l'itérateur ne recopie pas les objets cle, valeur, osef et vec_2.
*cough* je suis pas vraiment un programmeur de ouf, il y a peut-etre une méthode beaucoup plus propre pour faire ça. Si j'utilise Ruby, c'est parce que c'est simple à écrire. C'est la première fois que je me retrouve dans un cas où la lenteur joue contre moi. N'hésitez pas à me conseiller d'autres manières de faire si besoin.
# Tout est référence
Posté par yellowiscool . Évalué à 5.
Pour faire une copie, il faut utiliser la méthode clone.
Illustration:
>> a = 1
>> b = a
>> c = 3
>> a.object_id
=> 3
>> b.object_id
=> 3
>> c.object_id
=> 7
>> def salut(d)
>> d.object_id
>> end
>> salut a
=> 3
Envoyé depuis mon lapin.
[^] # Re: Tout est référence
Posté par Duncan Idaho . Évalué à 2.
[^] # Re: Tout est référence
Posté par castorpilot . Évalué à 2.
Ruby est en general plus lent que Perl, ne serait-ce que parce que l'interpreteur Perl est plus optimisé.
Ce probleme est connu de la communauté Ruby, et plusieurs projets tentent d'y remedier (cf. YARV, Jruby, ...).
# Héritage de C
Posté par Octabrain . Évalué à 1.
Prenons un cas plus simple. J'ai un tableau a = ["a","b","c"] et une fonction bete :
def rend_x (vecteur)
vecteur = ["x", "x", "x"]
end
Je voudrais appeler rend_x(a), et que a soit transformé en ["x", "x", "x"] (c'est-à-dire donc, que ma fonction modifie directement l'élément a -- j'insiste: c'est un exemple jouet, en pratique je m'en fout complètement, je veux juste qu'il ne recopie pas /a/ à l'appel de la fonction).
"
C'est dans ces cas-là que je pense qu'il est *nécessaire* d'avoir fait du C pour comprendre Ruby/Python/Java/autre-langage-impératif-commun.
Quand tu appelles "rend_x(foo)", dans ta fonction, "vecteur" et "foo" sont 2 variables distinctes, elles peuvent pointer au même endroit, et c'est le cas au début de ta fonction, mais elles peuvent aussi pointer des endroits différents, avec une simple affectation.
En l'occurence, quand tu fais "vecteur = ...", tu fais pointer "vecteur" vers d'autres données, sans modifier "foo" du coup. Tu alloues un nouveau tableau, totalement distinct de ton précédent ["a","b","c"], et les 2 ne sont pas à la même adresse.
(Note: à moins d'avoir des pointeurs sur pointeurs comme en C, tu ne peux pas modifier la variable "foo", mais tu peux modifier le contenu de là où elle pointe)
Si tu veux modifier le tableau, tu peux faire
vecteur[0] = "x"
vecteur[1] = "x"
vecteur[2] = "x"
En Python, il est possible de faire
vecteur[:] = ["x", "x", "x"]
qui va modifier en place le contenu de "vecteur" (il va le déréférencer), et qui ne va pas faire pointer "vecteur" vers un nouveau tableau. Un équivalent existe peut-être en Ruby, je ne connais pas assez.
Ou sinon un équivalent à :
vecteur.clear()
vecteur.extend(["x", "x", "x"])
Ou encore (méthode à éviter)
vecteur.clear()
vecteur += ["x", "x", "x"]
(qui n'est pas *du tout* équivalent à vecteur = vecteur + ["x", "x", "x"], car ce dernier ferait pointer vecteur autre part, alors que chez le premier "+=" est une opération en place pour les tableaux uniquement)
# Mauvais langage
Posté par Axioplase ıɥs∀ (site web personnel) . Évalué à 2.
Je crois vraiment que si tu te plains de la rapidité mais que tu insistes pour utiliser un langage de script, on ne peut plus rien pour toi.
À ta place, je piperais tout vers un filtre écrit en C (Ada, fortran, etc) (un filtre, c'est à dire un "while stdin != EOF").
Genre en entrée:
taille_paire_vecteurs
v1_1 v1_2 v1_3 … v1_taille_paire_vecteurs
v2_1 v2_2 v2_3 … v2_taille_paire_vecteurs
taille_paire_vecteurs
v1_1 v1_2 v1_3 … v1_taille_paire_vecteurs
v2_1 v2_2 v2_3 … v2_taille_paire_vecteurs
…
Et en sortie, ta mesure.
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.