Доступ к вложенному хешу в Perl HoH без использования ключей ()? - PullRequest
3 голосов
/ 09 ноября 2011

Рассмотрим следующий HoH:

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

Есть ли способ проверить, существует ли хэш-ключ на втором вложенном уровне без вызова keys(%$h)?Например, я хочу сказать что-то вроде:

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

(я понимаю, что вы не можете использовать * в качестве подстановочного знака хеш-ключа, но вы поняли идею ...)

Я пытаюсь избежать использования keys(), потому что он сбросит итератор хеша, и я перебираю цикл $h, используя:

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

Самая близкая языковая конструкция, которую я смог найти, это оператор умного совпадения (~~), упомянутый здесь (и не упоминается в perlref perldoc), но даже если ~~ был доступен в версии Perl, которую я вынужден использовать (5.8.4),из того, что я могу сказать, это не сработает в этом случае.

Если это не удастся сделать, я предполагаю, что я скопирую ключи в массив или хэш перед входом в мой цикл while (которыйкак я начал), но я надеялся избежать накладных расходов.

Ответы [ 5 ]

3 голосов
/ 09 ноября 2011

Не совсем.Если вам нужно это сделать, я думаю, что я бы создал объединенный хеш со списком всех ключей второго уровня (до запуска вашего основного цикла):

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

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

Тогда ваш exists($h->{*}->{1}) станет exists($all{1}).Конечно, это не сработает, если вы изменяете хеши второго уровня внутри цикла (если вы не обновите %all соответствующим образом).Код также предполагает, что все значения в $h являются хеш-значениями, но это будет легко исправить в случае необходимости.

2 голосов
/ 09 ноября 2011

Нет.each использует итератор хеша, и вы не можете выполнить итерацию по хешу, не используя его итератор, даже в C API.(Это означает, что интеллектуальное сопоставление не помогло бы в любом случае.)

Поскольку каждый хеш имеет свой собственный итератор, вы должны вызывать keys в том же хеше, который вы уже итерируете, используя each для запускаЭта проблема.Поскольку у вас нет проблем с вызовом keys для этого хэша, вы могли бы просто использовать keys вместо each?Или, может быть, один раз позвоните keys, сохраните результат, а затем переберите сохраненные ключи?

1 голос
/ 09 ноября 2011

Вы почти наверняка обнаружите, что издержки при агрегировании хэшей второго уровня меньше, чем у любого другого решения. Простой поиск хеша гораздо быстрее, чем итерация по всей структуре данных каждый раз, когда вы хотите выполнить проверку.

0 голосов
/ 09 ноября 2011

Почему бы не сделать это в самом Sybase вместо Perl?

Вы пытаетесь выполнить операцию установки , для чего в первую очередь создан Sybase.

Предполагая, что вы извлекли данные из таблицы со столбцами "key1", "key2", "valye" как "select *", просто выполните:

-- 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    

Любой запрос возвращает список ключей 1-го уровня, для которых key2 не равен 1

0 голосов
/ 09 ноября 2011

вы пытаетесь сделать это без цикла while? Вы можете проверить существование в хэше, просто ссылаясь на него, не генерируя ошибку

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

}
...