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

时间:2018-03-21 13:07:14

标签: set adapter nim

如何在“HashTable中的键视图”和nim中的HashSet之间执行set-difference操作?

概念:

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,它说“未声明的字段:'键'”哇。
但是,当然,这是无用的,我将循环并在这种状态下自己做出改变 我们需要的是一个无分配方法,它直接与hashset / hashtable一起工作。例如:

var diff = b - a.keys

或者最坏的情况:

var diff = b - HashSetView(a.keys)

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

可能?

编辑: 事实上,我只记得我头脑中浮现的东西,它是boost::transform_iterator概念 Initialize a container with iterator range of container with different type
这就是为什么在C ++的设计中,每个算法/ stdlib函数都需要一个范围(2个iters),而不是尽可能多地引用容器本身。这是鸭子打字的一种形式 第二个想法我的问题似乎是设置差异过程不适用于迭代器。

1 个答案:

答案 0 :(得分:3)

你正在寻找的东西似乎可以通过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

正如您所怀疑的那样,这比分配一个全新的序列只是为了丢弃一个集合更有效。似乎没有一种内置的方法来完成你想要完成的任务。

undeclared field: "keys"错误在keysan inline iterator且不是字段的上下文中有意义,但是此错误消息肯定可以提供更多信息。如果你想使用任意迭代器,你似乎必须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