设置 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
如何在 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