2011-11-08 1 views
3

Tenir compte de la HoH suivante:Accédez au hachage imbriqué dans Perl HoH sans utiliser de clés()?

$h = { 
    a => { 
      1 => x 
    }, 
    b => { 
      2 => y 
    }, 
    ... 
} 

est-il un moyen de vérifier si une clé de hachage existe sur le second niveau imbriqué sans appeler keys(%$h)? Par exemple, je veux dire quelque chose comme:

if (exists($h->{*}->{1})) { ... 

(Je sais que vous ne pouvez pas utiliser * comme caractère générique de clé de hachage, mais vous voyez l'idée ...)

Je suis en train de évitez d'utiliser keys() car il remet le compteur du hachage et j'itérer sur $h dans une boucle en utilisant:

while ((my ($key, $value) = each %$h)) { 
    ... 
} 

la construction de la langue la plus approchante est le smart match operator (~~) mentioned here (et aucune mention dans le perldoc perlref), mais même si ~~ était disponible dans la version de Perl Je suis contraint d'utiliser (5.8.4), d'après ce que je peux dire, cela ne fonctionnerait pas dans ce cas. Si cela ne peut pas être fait, je suppose que je vais copier les clés dans un tableau ou un hachage avant d'entrer dans ma boucle while (c'est ainsi que j'ai commencé), mais j'espérais éviter les frais généraux.

+0

Si vous copiez la liste des clés, vous n'avez plus besoin d'utiliser 'each% $ h' (malheureusement dangereux) et pouvez faire une boucle foreach à la place. Quelle est la taille de ce hachage? – Schwern

+0

@Schwern: environ 24 000 clés (somme de tous les hashs imbriqués). Il contient toutes les attributions de privilèges pour une base de données Sybase. Je me rends compte que je peux juste éviter 'each', ou copier les clés, ou trouver une autre implémentation. Je pensais juste poser la question pour voir si c'était possible. – MisterEd

+0

Je pense qu'il est possible de le faire efficacement, mais il faudrait changer l'interface ainsi que le code XS pour conserver l'itérateur de hachage après chaque itération au cas où il serait réinitialisé. La méthode 'each()' de perl5i a une telle interface, mais elle ne se défend pas contre votre cas. Il aimerait. https://github.com/schwern/perl5i/issues/210 – Schwern

Répondre

2

n ° each utilise l'itérateur du hachage, et vous ne pouvez pas itérer sur un hachage sans utiliser son itérateur, même dans l'API C. (Cela signifie que la correspondance intelligente ne serait d'aucune utilité de toute façon.

Étant donné que chaque table de hachage possède son propre itérateur, vous devez appeler keys sur le même hachage que vous itérez déjà en utilisant each pour résoudre ce problème. Puisque vous n'avez aucun problème en appelant keys sur ce hachage, pourriez-vous simplement utiliser keys au lieu de each? Ou peut-être appeler keys une fois, stocker le résultat, puis itérer sur les touches stockées?

+0

Merci, c'est la réponse définitive oui ou non que je cherchais. clés dans un hachage comme vous et @cjm suggéré (et comme je le faisais en premier lieu). – MisterEd

3

Pas vraiment. Si vous avez besoin de le faire, je pense que je crée une table de hachage fusionnée liste de toutes les clés du deuxième niveau (avant de commencer votre boucle principale):

my $h = { 
    a => { 
      1 => 'x' 
    }, 
    b => { 
      2 => 'y' 
    }, 
}; 

my %all = map { %$_ } values %$h; 

Ensuite, votre exists($h->{*}->{1}) devient exists($all{1}). Bien sûr, cela ne fonctionnera pas si vous modifiez les hachages de second niveau à l'intérieur de la boucle (sauf si vous mettez à jour correctement %all). Le code suppose également que toutes les valeurs dans $h sont des hashrefs, mais cela serait facile à corriger si nécessaire.

+0

Merci, c'est ainsi que j'ai commencé, mais j'ai enlevé le hachage supplémentaire parce que j'avais déjà les données dans un hachage et cela semblait inutile; sans parler du problème que vous avez mentionné d'avoir une autre structure à mettre à jour si les choses changent. – MisterEd

0

Essayez-vous de le faire sans boucle while? Vous pouvez tester l'existence d'un hachage juste en faisant référence, sans générer une erreur

while ( my ($key, $value) = each %{$h}) { 
    if ($value->{1}) { .. } 

} 
+0

Il n'essaie pas de déterminer si hashref_current_ a cette clé. Il essaie de déterminer si _any_ des hashrefs dans '$ h' a cette clé. – cjm

+0

n'aimez-vous pas Perl? mon $ oui; Foreach (trier {defined $ h -> {$ b} -> {1}? 1: 0} clés% $ h) {$ yes = 1 if (défini $ h -> {$ _} -> {1} ; last;} – ryansstack

+0

mais que l'utilisation des clés réinitialise l'itérateur – ysth

1

Vous trouverez certainement que le « frais généraux » de l'agrégation des hash de second niveau est inférieur à celui de toute autre solution. Une recherche de hachage simple est beaucoup plus rapide que l'itération sur l'ensemble de la structure de données chaque fois que vous souhaitez effectuer la vérification. Pourquoi ne pas le faire dans Sybase lui-même au lieu de Perl?

-1

Vous essayez de faire une opération qui est ce que Sybase est conçu pour faire en premier lieu.

En supposant que vous avez récupéré les données de la table avec des colonnes « key1 », « key2 », « valye » comme « select * », il suffit de faire:

-- Make sure mytable has index on key1 
SELECT key1 
FRIN mytable t1 
WHERE NOT EXISTS (
    SELECT 1 FROM mytable t2 
    WHERE t1.key1=t2.key1 
    AND t2.key2 = 1 
) 

----------- 
-- OR 
----------- 

SELECT DISTINCT key1 
INTO #t 
FROM mytable 

CREATE INDEX idx1_t on #t (key1) 

DELETE #t 
FROM mytable 
WHERE #t.key1=mytable.key1 
AND mytable.key2 = 1 

SELECT key1 from #t  

Ou requête renvoie une liste de clés 1er niveau qui ne possède pas la clé 2 de 1

+1

Parce que la question concerne Perl, Sybase est totalement hors de propos – dolmen

+0

@dolmen - google "problème XY" Il n'y a généralement pas de "problèmes Perl". Sybase semble être l'un des outils disponibles sur la base des commentaires de OP – DVK

Questions connexes