Модификация, вставка или удаление на месте в той же функции для карт ha sh в Rust - PullRequest
1 голос
/ 28 мая 2020

Допустим, у меня есть ha sh map m: HashMap<K, V>, ключ k: K и значение v: V, и я хотел бы сделать следующее:

  • Если m имеет не содержать значение в индексе k, вставить v в индекс k.
  • Если m содержит значение w в индексе k, примените функцию fn combine(x: V, y: V) -> Option<V> к v и w и:
    • Если результат None, удалите запись с индексом k из m.
    • Если результат Some(u), замените значение в индексе k на u.

Есть ли способ сделать это «на месте», без вызова функций, которые обращаются, изменяют или удаляют значение в k несколько раз?

Я также хотел бы избежать копирования данных, поэтому в идеале не нужно клонировать v, чтобы загружать клоны в insert и combine по отдельности.

Я мог бы переписать combine, чтобы использовать (изменяемые) ссылки (или встроить их), но wi sh отсутствия копирования данных все равно остается.

Ответы [ 2 ]

2 голосов
/ 29 мая 2020

Углубляясь в документацию Entry , я заметил, что варианты перечисления Entry предлагают функции для изменения, удаления или вставки записей на месте.

После взятия std::collections::hash_map::Entry, можно сделать следующее:

match m.entry(k) {
    Entry::Occupied(mut oe) => {
        let w = oe.get_mut();
        match combine(v, w) {
            Some(u) => { *w = u; },
            None    => { oe.remove_entry(); },
        }
    },
    Entry::Vacant(ve) => { ve.insert(v); },
}

( Здесь - Po C на игровой площадке Rust.)

Однако это , требует, чтобы combine принял (изменяемую) ссылку в качестве второго аргумента (что в моем случае нормально).

0 голосов
/ 29 мая 2020

Мне удалось сделать это за один доступ, одну запись и одно удаление ключа в худшем случае. Последнее удаление ключа не обязательно, но я не уверен, что это возможно. Я старался изо всех сил. Надеюсь, это поможет!

Хорошо, я думаю, что мы хотим использовать Entry API .

Полный список методов для Entry - здесь .

Думаю, мы бы сделали это в следующем порядке:

  • Если m содержит значение w с индексом k: (еще два шага )
  • Или вставьте v в индекс k.

Это можно сделать, используя .and_modify, а затем .or_insert. Что-то вроде этого:

let map = // ... Initialize the map

// Do stuff to it
// ...

// Our important bit:

let mut delete_entry = false;

map.entry(k)
   .and_modify(|w| { // If the entry exists, we modify it

     let u = combine(v, w);

     match u {

       Some(y) => *w = y;

       None => delete_entry = true;
       }
     }
   )
   .or_insert(v); // If it doesn't, we insert v

if delete_entry {
  map.remove(k);
}

Я не думаю, что есть способ сделать все три вещи без этого последнего map.remove доступа, так что это моя лучшая попытка на данный момент.

...