设置 HashTable 和 HashSet 的键之间的差异

set difference between keys of a HashTable and a HashSet

如何在 nim 中执行 "view of the keys from a HashTable" 和 HashSet 之间的集差操作?

概念上:

import tables, sets, sequtils

var a = initTable[int, string]()
var b: HashSet[int]
b.init(4)

a[0] = "a"
a[1] = "b"

b.incl(0)
b.incl(1)
b.incl(2)
b.incl(3)

var diff = b - toset(toseq(a.keys))
echo diff # {2, 3}

这行得通(而且很难行得通,编译器给出了误导性信息。例如,尝试删除上面的 toseq,它说 "undeclared field: 'keys'" 哇。)
但是,当然,这是没有用的,我会循环并在这种情况下自己做出改变。
我们需要的是一种直接与 hashset/hashtable 一起工作的无分配方法。例如:

var diff = b - a.keys

或最坏情况:

var diff = b - HashSetView(a.keys)

这将从键迭代器创建一个适配器对象以适应可能只接受集合的 - 过程。

可能吗?

编辑: 其实我只是想起来一直在我脑海里浮现的东西,就是boost::transform_iterator这个概念。

这就是为什么在 C++ 的设计中,每个 algorithm/stdlib 函数都采用一个范围(2 个迭代器)并且尽可能不引用容器本身。这是鸭子打字的一种形式。
转念一想,我的问题似乎是集差过程不适用于迭代器。

Nim 的元编程和 UFCS 工具似乎可以很好地解决您正在寻找的问题:

proc `-`[K, V](a: HashSet[K], b: Table[K, V]): HashSet[K] =
  result = a

  for k in b.keys:
    result.excl k


var diff = b - a

正如您所怀疑的那样,这比分配一个全新的序列只是为了将其丢弃为一个集合要更高效。不过,似乎没有 built-in 方法可以完成您想要完成的事情。

undeclared field: "keys" 错误在 keys is an inline iterator and not a field, however this error message certainly could be more informative. If you wish to use an arbitrary iterator, it seems like you will have to wrap it in a closure 的上下文中是有意义的,尽管它可能会因此引入一些开销。

使用 nim-iterutils 包中的 toClosure

template toClosure*(i): auto =
  ## Wrap an inline iterator in a first-class closure iterator.
  iterator j: type(i) {.closure.} =
    for x in i:
      yield x
  j

proc `-`[K](a: HashSet[K], b: iterator): HashSet[K] =
  result = a

  for k in b():
    result.excl k


var diff = b - toClosure(a.keys)

如果您不需要完整的结果,那么产生差异可能会有更好的性能。

iterator without_keys[K, V](a: HashSet[K], b: Table[K, V]): K =
  for k in a.items:
    if not b.has_key(k):
      yield k

for k in b.without_keys(a):
  echo k