diff options
Diffstat (limited to 'lib/pure/collections')
-rw-r--r-- | lib/pure/collections/critbits.nim | 378 | ||||
-rw-r--r-- | lib/pure/collections/deques.nim | 625 | ||||
-rw-r--r-- | lib/pure/collections/hashcommon.nim | 29 | ||||
-rw-r--r-- | lib/pure/collections/heapqueue.nim | 305 | ||||
-rw-r--r-- | lib/pure/collections/intsets.nim | 675 | ||||
-rw-r--r-- | lib/pure/collections/lists.nim | 795 | ||||
-rw-r--r-- | lib/pure/collections/sequtils.nim | 541 | ||||
-rw-r--r-- | lib/pure/collections/setimpl.nim | 11 | ||||
-rw-r--r-- | lib/pure/collections/sets.nim | 448 | ||||
-rw-r--r-- | lib/pure/collections/sharedlist.nim | 22 | ||||
-rw-r--r-- | lib/pure/collections/sharedtables.nim | 137 | ||||
-rw-r--r-- | lib/pure/collections/tableimpl.nim | 116 | ||||
-rw-r--r-- | lib/pure/collections/tables.nim | 1454 |
13 files changed, 2322 insertions, 3214 deletions
diff --git a/lib/pure/collections/critbits.nim b/lib/pure/collections/critbits.nim index 91c113988..24257dacb 100644 --- a/lib/pure/collections/critbits.nim +++ b/lib/pure/collections/critbits.nim @@ -8,12 +8,37 @@ # ## This module implements a `crit bit tree`:idx: which is an efficient -## container for a sorted set of strings, or for a sorted mapping of strings. Based on the excellent paper -## by Adam Langley. +## container for a sorted set of strings, or for a sorted mapping of strings. Based on the +## [excellent paper by Adam Langley](https://www.imperialviolet.org/binary/critbit.pdf). ## (A crit bit tree is a form of `radix tree`:idx: or `patricia trie`:idx:.) +runnableExamples: + from std/sequtils import toSeq + + var critbitAsSet: CritBitTree[void] = ["kitten", "puppy"].toCritBitTree + doAssert critbitAsSet.len == 2 + critbitAsSet.incl("") + doAssert "" in critbitAsSet + critbitAsSet.excl("") + doAssert "" notin critbitAsSet + doAssert toSeq(critbitAsSet.items) == @["kitten", "puppy"] + let same = ["puppy", "kitten", "puppy"].toCritBitTree + doAssert toSeq(same.keys) == toSeq(critbitAsSet.keys) + + var critbitAsDict: CritBitTree[int] = {"key1": 42}.toCritBitTree + doAssert critbitAsDict.len == 1 + critbitAsDict["key2"] = 0 + doAssert "key2" in critbitAsDict + doAssert critbitAsDict["key2"] == 0 + critbitAsDict.excl("key1") + doAssert "key1" notin critbitAsDict + doAssert toSeq(critbitAsDict.pairs) == @[("key2", 0)] + import std/private/since +when defined(nimPreviewSlimSystem): + import std/assertions + type NodeObj[T] {.acyclic.} = object byte: int ## byte index of the difference @@ -28,17 +53,15 @@ type Node[T] = ref NodeObj[T] CritBitTree*[T] = object ## The crit bit tree can either be used ## as a mapping from strings to - ## some type ``T`` or as a set of - ## strings if ``T`` is void. + ## some type `T` or as a set of + ## strings if `T` is `void`. root: Node[T] count: int -proc len*[T](c: CritBitTree[T]): int = +func len*[T](c: CritBitTree[T]): int {.inline.} = ## Returns the number of elements in `c` in O(1). runnableExamples: - var c: CritBitTree[void] - incl(c, "key1") - incl(c, "key2") + let c = ["key1", "key2"].toCritBitTree doAssert c.len == 2 result = c.count @@ -53,7 +76,7 @@ proc rawGet[T](c: CritBitTree[T], key: string): Node[T] = else: return if it.key == key: it else: nil -proc contains*[T](c: CritBitTree[T], key: string): bool {.inline.} = +func contains*[T](c: CritBitTree[T], key: string): bool {.inline.} = ## Returns true if `c` contains the given `key`. runnableExamples: var c: CritBitTree[void] @@ -62,7 +85,7 @@ proc contains*[T](c: CritBitTree[T], key: string): bool {.inline.} = result = rawGet(c, key) != nil -proc hasKey*[T](c: CritBitTree[T], key: string): bool {.inline.} = +func hasKey*[T](c: CritBitTree[T], key: string): bool {.inline.} = ## Alias for `contains <#contains,CritBitTree[T],string>`_. result = rawGet(c, key) != nil @@ -116,7 +139,7 @@ proc rawInsert[T](c: var CritBitTree[T], key: string): Node[T] = wherep[] = inner inc c.count -proc exclImpl[T](c: var CritBitTree[T], key: string): int = +func exclImpl[T](c: var CritBitTree[T], key: string): int = var p = c.root var wherep = addr(c.root) var whereq: ptr Node[T] = nil @@ -144,7 +167,7 @@ proc excl*[T](c: var CritBitTree[T], key: string) = ## Removes `key` (and its associated value) from the set `c`. ## If the `key` does not exist, nothing happens. ## - ## See also: + ## **See also:** ## * `incl proc <#incl,CritBitTree[void],string>`_ ## * `incl proc <#incl,CritBitTree[T],string,T>`_ runnableExamples: @@ -157,9 +180,9 @@ proc excl*[T](c: var CritBitTree[T], key: string) = proc missingOrExcl*[T](c: var CritBitTree[T], key: string): bool = ## Returns true if `c` does not contain the given `key`. If the key - ## does exist, c.excl(key) is performed. + ## does exist, `c.excl(key)` is performed. ## - ## See also: + ## **See also:** ## * `excl proc <#excl,CritBitTree[T],string>`_ ## * `containsOrIncl proc <#containsOrIncl,CritBitTree[T],string,T>`_ ## * `containsOrIncl proc <#containsOrIncl,CritBitTree[void],string>`_ @@ -177,11 +200,11 @@ proc missingOrExcl*[T](c: var CritBitTree[T], key: string): bool = discard exclImpl(c, key) result = c.count == oldCount -proc containsOrIncl*[T](c: var CritBitTree[T], key: string, val: T): bool = - ## Returns true if `c` contains the given `key`. If the key does not exist - ## ``c[key] = val`` is performed. +proc containsOrIncl*[T](c: var CritBitTree[T], key: string, val: sink T): bool = + ## Returns true if `c` contains the given `key`. If the key does not exist, + ## `c[key] = val` is performed. ## - ## See also: + ## **See also:** ## * `incl proc <#incl,CritBitTree[void],string>`_ ## * `incl proc <#incl,CritBitTree[T],string,T>`_ ## * `containsOrIncl proc <#containsOrIncl,CritBitTree[void],string>`_ @@ -204,10 +227,10 @@ proc containsOrIncl*[T](c: var CritBitTree[T], key: string, val: T): bool = if not result: n.val = val proc containsOrIncl*(c: var CritBitTree[void], key: string): bool = - ## Returns true if `c` contains the given `key`. If the key does not exist + ## Returns true if `c` contains the given `key`. If the key does not exist, ## it is inserted into `c`. ## - ## See also: + ## **See also:** ## * `incl proc <#incl,CritBitTree[void],string>`_ ## * `incl proc <#incl,CritBitTree[T],string,T>`_ ## * `containsOrIncl proc <#containsOrIncl,CritBitTree[T],string,T>`_ @@ -240,7 +263,7 @@ proc inc*(c: var CritBitTree[int]; key: string, val: int = 1) = proc incl*(c: var CritBitTree[void], key: string) = ## Includes `key` in `c`. ## - ## See also: + ## **See also:** ## * `excl proc <#excl,CritBitTree[T],string>`_ ## * `incl proc <#incl,CritBitTree[T],string,T>`_ runnableExamples: @@ -250,10 +273,10 @@ proc incl*(c: var CritBitTree[void], key: string) = discard rawInsert(c, key) -proc incl*[T](c: var CritBitTree[T], key: string, val: T) = +proc incl*[T](c: var CritBitTree[T], key: string, val: sink T) = ## Inserts `key` with value `val` into `c`. ## - ## See also: + ## **See also:** ## * `excl proc <#excl,CritBitTree[T],string>`_ ## * `incl proc <#incl,CritBitTree[void],string>`_ runnableExamples: @@ -264,45 +287,37 @@ proc incl*[T](c: var CritBitTree[T], key: string, val: T) = var n = rawInsert(c, key) n.val = val -proc `[]=`*[T](c: var CritBitTree[T], key: string, val: T) = - ## Puts a (key, value)-pair into `t`. +proc `[]=`*[T](c: var CritBitTree[T], key: string, val: sink T) = + ## Alias for `incl <#incl,CritBitTree[T],string,T>`_. ## - ## See also: + ## **See also:** ## * `[] proc <#[],CritBitTree[T],string>`_ ## * `[] proc <#[],CritBitTree[T],string_2>`_ - runnableExamples: - var c: CritBitTree[int] - c["key"] = 42 - doAssert c["key"] == 42 - var n = rawInsert(c, key) n.val = val template get[T](c: CritBitTree[T], key: string): T = let n = rawGet(c, key) if n == nil: - when compiles($key): - raise newException(KeyError, "key not found: " & $key) - else: - raise newException(KeyError, "key not found") + raise newException(KeyError, "key not found: " & key) n.val -proc `[]`*[T](c: CritBitTree[T], key: string): T {.inline.} = - ## Retrieves the value at ``c[key]``. If `key` is not in `t`, the - ## ``KeyError`` exception is raised. One can check with ``hasKey`` whether +func `[]`*[T](c: CritBitTree[T], key: string): lent T {.inline.} = + ## Retrieves the value at `c[key]`. If `key` is not in `t`, the + ## `KeyError` exception is raised. One can check with `hasKey` whether ## the key exists. ## - ## See also: + ## **See also:** ## * `[] proc <#[],CritBitTree[T],string_2>`_ ## * `[]= proc <#[]=,CritBitTree[T],string,T>`_ get(c, key) -proc `[]`*[T](c: var CritBitTree[T], key: string): var T {.inline.} = - ## Retrieves the value at ``c[key]``. The value can be modified. - ## If `key` is not in `t`, the ``KeyError`` exception is raised. +func `[]`*[T](c: var CritBitTree[T], key: string): var T {.inline.} = + ## Retrieves the value at `c[key]`. The value can be modified. + ## If `key` is not in `t`, the `KeyError` exception is raised. ## - ## See also: + ## **See also:** ## * `[] proc <#[],CritBitTree[T],string>`_ ## * `[]= proc <#[]=,CritBitTree[T],string,T>`_ get(c, key) @@ -323,27 +338,24 @@ iterator leaves[T](n: Node[T]): Node[T] = iterator keys*[T](c: CritBitTree[T]): string = ## Yields all keys in lexicographical order. runnableExamples: - var c: CritBitTree[int] - c["key1"] = 1 - c["key2"] = 2 - var keys: seq[string] - for key in c.keys: - keys.add(key) - doAssert keys == @["key1", "key2"] + from std/sequtils import toSeq + + let c = {"key1": 1, "key2": 2}.toCritBitTree + doAssert toSeq(c.keys) == @["key1", "key2"] for x in leaves(c.root): yield x.key -iterator values*[T](c: CritBitTree[T]): T = +iterator values*[T](c: CritBitTree[T]): lent T = ## Yields all values of `c` in the lexicographical order of the ## corresponding keys. + ## + ## **See also:** + ## * `mvalues iterator <#mvalues.i,CritBitTree[T]>`_ runnableExamples: - var c: CritBitTree[int] - c["key1"] = 1 - c["key2"] = 2 - var vals: seq[int] - for val in c.values: - vals.add(val) - doAssert vals == @[1, 2] + from std/sequtils import toSeq + + let c = {"key1": 1, "key2": 2}.toCritBitTree + doAssert toSeq(c.values) == @[1, 2] for x in leaves(c.root): yield x.val @@ -351,45 +363,37 @@ iterator mvalues*[T](c: var CritBitTree[T]): var T = ## Yields all values of `c` in the lexicographical order of the ## corresponding keys. The values can be modified. ## - ## See also: + ## **See also:** ## * `values iterator <#values.i,CritBitTree[T]>`_ for x in leaves(c.root): yield x.val iterator items*[T](c: CritBitTree[T]): string = - ## Yields all keys in lexicographical order. - runnableExamples: - var c: CritBitTree[int] - c["key1"] = 1 - c["key2"] = 2 - var keys: seq[string] - for key in c.items: - keys.add(key) - doAssert keys == @["key1", "key2"] - + ## Alias for `keys <#keys.i,CritBitTree[T]>`_. for x in leaves(c.root): yield x.key iterator pairs*[T](c: CritBitTree[T]): tuple[key: string, val: T] = - ## Yields all (key, value)-pairs of `c`. + ## Yields all `(key, value)`-pairs of `c` in the lexicographical order of the + ## corresponding keys. + ## + ## **See also:** + ## * `mpairs iterator <#mpairs.i,CritBitTree[T]>`_ runnableExamples: - var c: CritBitTree[int] - c["key1"] = 1 - c["key2"] = 2 - var ps: seq[tuple[key: string, val: int]] - for p in c.pairs: - ps.add(p) - doAssert ps == @[(key: "key1", val: 1), (key: "key2", val: 2)] + from std/sequtils import toSeq + + let c = {"key1": 1, "key2": 2}.toCritBitTree + doAssert toSeq(c.pairs) == @[(key: "key1", val: 1), (key: "key2", val: 2)] for x in leaves(c.root): yield (x.key, x.val) iterator mpairs*[T](c: var CritBitTree[T]): tuple[key: string, val: var T] = - ## Yields all (key, value)-pairs of `c`. The yielded values can be modified. + ## Yields all `(key, value)`-pairs of `c` in the lexicographical order of the + ## corresponding keys. The yielded values can be modified. ## - ## See also: + ## **See also:** ## * `pairs iterator <#pairs.i,CritBitTree[T]>`_ for x in leaves(c.root): yield (x.key, x.val) -proc allprefixedAux[T](c: CritBitTree[T], key: string; - longestMatch: bool): Node[T] = +proc allprefixedAux[T](c: CritBitTree[T], key: string): Node[T] = var p = c.root var top = p if p != nil: @@ -399,100 +403,83 @@ proc allprefixedAux[T](c: CritBitTree[T], key: string; let dir = (1 + (ch.ord or p.otherBits.ord)) shr 8 p = p.child[dir] if q.byte < key.len: top = p - if not longestMatch: - for i in 0 ..< key.len: - if i >= p.key.len or p.key[i] != key[i]: return + for i in 0 ..< key.len: + if i >= p.key.len or p.key[i] != key[i]: return result = top -iterator itemsWithPrefix*[T](c: CritBitTree[T], prefix: string; - longestMatch = false): string = - ## Yields all keys starting with `prefix`. If `longestMatch` is true, - ## the longest match is returned, it doesn't have to be a complete match then. - runnableExamples: - var c: CritBitTree[int] - c["key1"] = 42 - c["key2"] = 43 - var keys: seq[string] - for key in c.itemsWithPrefix("key"): - keys.add(key) - doAssert keys == @["key1", "key2"] - - let top = allprefixedAux(c, prefix, longestMatch) - for x in leaves(top): yield x.key - -iterator keysWithPrefix*[T](c: CritBitTree[T], prefix: string; - longestMatch = false): string = +iterator keysWithPrefix*[T](c: CritBitTree[T], prefix: string): string = ## Yields all keys starting with `prefix`. runnableExamples: - var c: CritBitTree[int] - c["key1"] = 42 - c["key2"] = 43 - var keys: seq[string] - for key in c.keysWithPrefix("key"): - keys.add(key) - doAssert keys == @["key1", "key2"] - - let top = allprefixedAux(c, prefix, longestMatch) + from std/sequtils import toSeq + + let c = {"key1": 42, "key2": 43}.toCritBitTree + doAssert toSeq(c.keysWithPrefix("key")) == @["key1", "key2"] + + let top = allprefixedAux(c, prefix) for x in leaves(top): yield x.key -iterator valuesWithPrefix*[T](c: CritBitTree[T], prefix: string; - longestMatch = false): T = +iterator valuesWithPrefix*[T](c: CritBitTree[T], prefix: string): lent T = ## Yields all values of `c` starting with `prefix` of the ## corresponding keys. + ## + ## **See also:** + ## * `mvaluesWithPrefix iterator <#mvaluesWithPrefix.i,CritBitTree[T],string>`_ runnableExamples: - var c: CritBitTree[int] - c["key1"] = 42 - c["key2"] = 43 - var vals: seq[int] - for val in c.valuesWithPrefix("key"): - vals.add(val) - doAssert vals == @[42, 43] - - let top = allprefixedAux(c, prefix, longestMatch) + from std/sequtils import toSeq + + let c = {"key1": 42, "key2": 43}.toCritBitTree + doAssert toSeq(c.valuesWithPrefix("key")) == @[42, 43] + + let top = allprefixedAux(c, prefix) for x in leaves(top): yield x.val -iterator mvaluesWithPrefix*[T](c: var CritBitTree[T], prefix: string; - longestMatch = false): var T = +iterator mvaluesWithPrefix*[T](c: var CritBitTree[T], prefix: string): var T = ## Yields all values of `c` starting with `prefix` of the ## corresponding keys. The values can be modified. ## - ## See also: + ## **See also:** ## * `valuesWithPrefix iterator <#valuesWithPrefix.i,CritBitTree[T],string>`_ - let top = allprefixedAux(c, prefix, longestMatch) + let top = allprefixedAux(c, prefix) for x in leaves(top): yield x.val +iterator itemsWithPrefix*[T](c: CritBitTree[T], prefix: string): string = + ## Alias for `keysWithPrefix <#keysWithPrefix.i,CritBitTree[T],string>`_. + let top = allprefixedAux(c, prefix) + for x in leaves(top): yield x.key + iterator pairsWithPrefix*[T](c: CritBitTree[T], - prefix: string; - longestMatch = false): tuple[key: string, val: T] = + prefix: string): tuple[key: string, val: T] = ## Yields all (key, value)-pairs of `c` starting with `prefix`. + ## + ## **See also:** + ## * `mpairsWithPrefix iterator <#mpairsWithPrefix.i,CritBitTree[T],string>`_ runnableExamples: - var c: CritBitTree[int] - c["key1"] = 42 - c["key2"] = 43 - var ps: seq[tuple[key: string, val: int]] - for p in c.pairsWithPrefix("key"): - ps.add(p) - doAssert ps == @[(key: "key1", val: 42), (key: "key2", val: 43)] - - let top = allprefixedAux(c, prefix, longestMatch) + from std/sequtils import toSeq + + let c = {"key1": 42, "key2": 43}.toCritBitTree + doAssert toSeq(c.pairsWithPrefix("key")) == @[(key: "key1", val: 42), (key: "key2", val: 43)] + + let top = allprefixedAux(c, prefix) for x in leaves(top): yield (x.key, x.val) iterator mpairsWithPrefix*[T](c: var CritBitTree[T], - prefix: string; - longestMatch = false): tuple[key: string, val: var T] = + prefix: string): tuple[key: string, val: var T] = ## Yields all (key, value)-pairs of `c` starting with `prefix`. ## The yielded values can be modified. ## - ## See also: + ## **See also:** ## * `pairsWithPrefix iterator <#pairsWithPrefix.i,CritBitTree[T],string>`_ - let top = allprefixedAux(c, prefix, longestMatch) + let top = allprefixedAux(c, prefix) for x in leaves(top): yield (x.key, x.val) -proc `$`*[T](c: CritBitTree[T]): string = - ## Turns `c` into a string representation. Example outputs: - ## ``{keyA: value, keyB: value}``, ``{:}`` - ## If `T` is void the outputs look like: - ## ``{keyA, keyB}``, ``{}``. +func `$`*[T](c: CritBitTree[T]): string = + ## Turns `c` into a string representation. + runnableExamples: + doAssert $CritBitTree[int].default == "{:}" + doAssert $toCritBitTree({"key1": 1, "key2": 2}) == """{"key1": 1, "key2": 2}""" + doAssert $CritBitTree[void].default == "{}" + doAssert $toCritBitTree(["key1", "key2"]) == """{"key1", "key2"}""" + if c.len == 0: when T is void: result = "{}" @@ -518,8 +505,8 @@ proc `$`*[T](c: CritBitTree[T]): string = result.addQuoted(val) result.add("}") -proc commonPrefixLen*[T](c: CritBitTree[T]): int {.inline, since((1, 3)).} = - ## Returns longest common prefix length of all keys of `c`. +func commonPrefixLen*[T](c: CritBitTree[T]): int {.inline, since((1, 3)).} = + ## Returns the length of the longest common prefix of all keys in `c`. ## If `c` is empty, returns 0. runnableExamples: var c: CritBitTree[void] @@ -534,90 +521,17 @@ proc commonPrefixLen*[T](c: CritBitTree[T]): int {.inline, since((1, 3)).} = else: c.root.byte else: 0 +proc toCritBitTree*[T](pairs: sink openArray[(string, T)]): CritBitTree[T] {.since: (1, 3).} = + ## Creates a new `CritBitTree` that contains the given `pairs`. + runnableExamples: + doAssert {"a": "0", "b": "1", "c": "2"}.toCritBitTree is CritBitTree[string] + doAssert {"a": 0, "b": 1, "c": 2}.toCritBitTree is CritBitTree[int] -runnableExamples: - static: - block: - var critbitAsSet: CritBitTree[void] - doAssert critbitAsSet.len == 0 - incl critbitAsSet, "kitten" - doAssert critbitAsSet.len == 1 - incl critbitAsSet, "puppy" - doAssert critbitAsSet.len == 2 - incl critbitAsSet, "kitten" - doAssert critbitAsSet.len == 2 - incl critbitAsSet, "" - doAssert critbitAsSet.len == 3 - block: - var critbitAsDict: CritBitTree[int] - critbitAsDict["key"] = 42 - doAssert critbitAsDict["key"] == 42 - critbitAsDict["key"] = 0 - doAssert critbitAsDict["key"] == 0 - critbitAsDict["key"] = -int.high - doAssert critbitAsDict["key"] == -int.high - critbitAsDict["key"] = int.high - doAssert critbitAsDict["key"] == int.high - - -when isMainModule: - import sequtils - - var r: CritBitTree[void] - r.incl "abc" - r.incl "xyz" - r.incl "def" - r.incl "definition" - r.incl "prefix" - r.incl "foo" - - doAssert r.contains"def" - - r.excl "def" - assert r.missingOrExcl("foo") == false - assert "foo" notin toSeq(r.items) - - assert r.missingOrExcl("foo") == true - - assert toSeq(r.items) == @["abc", "definition", "prefix", "xyz"] - - assert toSeq(r.itemsWithPrefix("de")) == @["definition"] - var c = CritBitTree[int]() - - c.inc("a") - assert c["a"] == 1 - - c.inc("a", 4) - assert c["a"] == 5 - - c.inc("a", -5) - assert c["a"] == 0 - - c.inc("b", 2) - assert c["b"] == 2 - - c.inc("c", 3) - assert c["c"] == 3 - - c.inc("a", 1) - assert c["a"] == 1 - - var cf = CritBitTree[float]() - - cf.incl("a", 1.0) - assert cf["a"] == 1.0 - - cf.incl("b", 2.0) - assert cf["b"] == 2.0 - - cf.incl("c", 3.0) - assert cf["c"] == 3.0 + for item in pairs: result.incl item[0], item[1] - assert cf.len == 3 - cf.excl("c") - assert cf.len == 2 +proc toCritBitTree*(items: sink openArray[string]): CritBitTree[void] {.since: (1, 3).} = + ## Creates a new `CritBitTree` that contains the given `items`. + runnableExamples: + doAssert ["a", "b", "c"].toCritBitTree is CritBitTree[void] - var cb: CritBitTree[string] - cb.incl("help", "help") - for k in cb.keysWithPrefix("helpp"): - doAssert false, "there is no prefix helpp" + for item in items: result.incl item diff --git a/lib/pure/collections/deques.nim b/lib/pure/collections/deques.nim index d096874a3..d2b0099f2 100644 --- a/lib/pure/collections/deques.nim +++ b/lib/pure/collections/deques.nim @@ -7,157 +7,153 @@ # distribution, for details about the copyright. # -## Implementation of a `deque`:idx: (double-ended queue). -## The underlying implementation uses a ``seq``. +## An implementation of a `deque`:idx: (double-ended queue). +## The underlying implementation uses a `seq`. ## -## None of the procs that get an individual value from the deque can be used -## on an empty deque. -## If compiled with `boundChecks` option, those procs will raise an `IndexDefect` -## on such access. This should not be relied upon, as `-d:release` will -## disable those checks and may return garbage or crash the program. +## .. note:: None of the procs that get an individual value from the deque should be used +## on an empty deque. +## +## If compiled with the `boundChecks` option, those procs will raise an `IndexDefect` +## on such access. This should not be relied upon, as `-d:danger` or `--checks:off` will +## disable those checks and then the procs may return garbage or crash the program. ## ## As such, a check to see if the deque is empty is needed before any ## access, unless your program logic guarantees it indirectly. -## -## .. code-block:: Nim -## import deques -## -## var a = initDeque[int]() -## -## doAssertRaises(IndexDefect, echo a[0]) -## -## for i in 1 .. 5: -## a.addLast(10*i) -## assert $a == "[10, 20, 30, 40, 50]" -## -## assert a.peekFirst == 10 -## assert a.peekLast == 50 -## assert len(a) == 5 -## -## assert a.popFirst == 10 -## assert a.popLast == 50 -## assert len(a) == 3 -## -## a.addFirst(11) -## a.addFirst(22) -## a.addFirst(33) -## assert $a == "[33, 22, 11, 20, 30, 40]" -## -## a.shrink(fromFirst = 1, fromLast = 2) -## assert $a == "[22, 11, 20]" -## -## -## **See also:** + +runnableExamples: + var a = [10, 20, 30, 40].toDeque + + doAssertRaises(IndexDefect, echo a[4]) + + a.addLast(50) + assert $a == "[10, 20, 30, 40, 50]" + + assert a.peekFirst == 10 + assert a.peekLast == 50 + assert len(a) == 5 + + assert a.popFirst == 10 + assert a.popLast == 50 + assert len(a) == 3 + + a.addFirst(11) + a.addFirst(22) + a.addFirst(33) + assert $a == "[33, 22, 11, 20, 30, 40]" + + a.shrink(fromFirst = 1, fromLast = 2) + assert $a == "[22, 11, 20]" + +## See also +## ======== ## * `lists module <lists.html>`_ for singly and doubly linked lists and rings -## * `channels module <channels.html>`_ for inter-thread communication import std/private/since -import math +import std/[assertions, hashes, math] type Deque*[T] = object - ## A double-ended queue backed with a ringed seq buffer. + ## A double-ended queue backed with a ringed `seq` buffer. ## - ## To initialize an empty deque use `initDeque proc <#initDeque,int>`_. + ## To initialize an empty deque, + ## use the `initDeque proc <#initDeque,int>`_. data: seq[T] - head, tail, count, mask: int + + # `head` and `tail` are masked only when accessing an element of `data` + # so that `tail - head == data.len` when the deque is full. + # They are uint so that incrementing/decrementing them doesn't cause + # over/underflow. You can get a number of items with `tail - head` + # even if `tail` or `head` is wraps around and `tail < head`, because + # `tail - head == (uint.high + 1 + tail) - head` when `tail < head`. + head, tail: uint const defaultInitialSize* = 4 template initImpl(result: typed, initialSize: int) = - assert isPowerOfTwo(initialSize) - result.mask = initialSize-1 - newSeq(result.data, initialSize) + let correctSize = nextPowerOfTwo(initialSize) + newSeq(result.data, correctSize) template checkIfInitialized(deq: typed) = - when compiles(defaultInitialSize): - if deq.mask == 0: - initImpl(deq, defaultInitialSize) + if deq.data.len == 0: + initImpl(deq, defaultInitialSize) + +func mask[T](deq: Deque[T]): uint {.inline.} = + uint(deq.data.len) - 1 -proc initDeque*[T](initialSize: int = 4): Deque[T] = - ## Create a new empty deque. +proc initDeque*[T](initialSize: int = defaultInitialSize): Deque[T] = + ## Creates a new empty deque. ## ## Optionally, the initial capacity can be reserved via `initialSize` - ## as a performance optimization. + ## as a performance optimization + ## (default: `defaultInitialSize <#defaultInitialSize>`_). ## The length of a newly created deque will still be 0. ## - ## ``initialSize`` must be a power of two (default: 4). - ## If you need to accept runtime values for this you could use the - ## `nextPowerOfTwo proc<math.html#nextPowerOfTwo,int>`_ from the - ## `math module<math.html>`_. + ## **See also:** + ## * `toDeque proc <#toDeque,openArray[T]>`_ result.initImpl(initialSize) -proc len*[T](deq: Deque[T]): int {.inline.} = - ## Return the number of elements of `deq`. - result = deq.count +func len*[T](deq: Deque[T]): int {.inline.} = + ## Returns the number of elements of `deq`. + int(deq.tail - deq.head) template emptyCheck(deq) = # Bounds check for the regular deque access. when compileOption("boundChecks"): - if unlikely(deq.count < 1): + if unlikely(deq.len < 1): raise newException(IndexDefect, "Empty deque.") template xBoundsCheck(deq, i) = # Bounds check for the array like accesses. - when compileOption("boundChecks"): # d:release should disable this. - if unlikely(i >= deq.count): # x < deq.low is taken care by the Natural parameter + when compileOption("boundChecks"): # `-d:danger` or `--checks:off` should disable this. + if unlikely(i >= deq.len): # x < deq.low is taken care by the Natural parameter raise newException(IndexDefect, - "Out of bounds: " & $i & " > " & $(deq.count - 1)) + "Out of bounds: " & $i & " > " & $(deq.len - 1)) if unlikely(i < 0): # when used with BackwardsIndex raise newException(IndexDefect, "Out of bounds: " & $i & " < 0") -proc `[]`*[T](deq: Deque[T], i: Natural): T {.inline.} = - ## Access the i-th element of `deq`. +proc `[]`*[T](deq: Deque[T], i: Natural): lent T {.inline.} = + ## Accesses the `i`-th element of `deq`. runnableExamples: - var a = initDeque[int]() - for i in 1 .. 5: - a.addLast(10*i) + let a = [10, 20, 30, 40, 50].toDeque assert a[0] == 10 assert a[3] == 40 doAssertRaises(IndexDefect, echo a[8]) xBoundsCheck(deq, i) - return deq.data[(deq.head + i) and deq.mask] + return deq.data[(deq.head + i.uint) and deq.mask] proc `[]`*[T](deq: var Deque[T], i: Natural): var T {.inline.} = - ## Access the i-th element of `deq` and return a mutable + ## Accesses the `i`-th element of `deq` and returns a mutable ## reference to it. runnableExamples: - var a = initDeque[int]() - for i in 1 .. 5: - a.addLast(10*i) - assert a[0] == 10 - assert a[3] == 40 - doAssertRaises(IndexDefect, echo a[8]) + var a = [10, 20, 30, 40, 50].toDeque + inc(a[0]) + assert a[0] == 11 xBoundsCheck(deq, i) - return deq.data[(deq.head + i) and deq.mask] + return deq.data[(deq.head + i.uint) and deq.mask] -proc `[]=`*[T](deq: var Deque[T], i: Natural, val: T) {.inline.} = - ## Change the i-th element of `deq`. +proc `[]=`*[T](deq: var Deque[T], i: Natural, val: sink T) {.inline.} = + ## Sets the `i`-th element of `deq` to `val`. runnableExamples: - var a = initDeque[int]() - for i in 1 .. 5: - a.addLast(10*i) + var a = [10, 20, 30, 40, 50].toDeque a[0] = 99 a[3] = 66 assert $a == "[99, 20, 30, 66, 50]" checkIfInitialized(deq) xBoundsCheck(deq, i) - deq.data[(deq.head + i) and deq.mask] = val + deq.data[(deq.head + i.uint) and deq.mask] = val -proc `[]`*[T](deq: Deque[T], i: BackwardsIndex): T {.inline.} = - ## Access the backwards indexed i-th element. +proc `[]`*[T](deq: Deque[T], i: BackwardsIndex): lent T {.inline.} = + ## Accesses the backwards indexed `i`-th element. ## ## `deq[^1]` is the last element. runnableExamples: - var a = initDeque[int]() - for i in 1 .. 5: - a.addLast(10*i) + let a = [10, 20, 30, 40, 50].toDeque assert a[^1] == 50 assert a[^4] == 20 doAssertRaises(IndexDefect, echo a[^9]) @@ -166,28 +162,24 @@ proc `[]`*[T](deq: Deque[T], i: BackwardsIndex): T {.inline.} = return deq[deq.len - int(i)] proc `[]`*[T](deq: var Deque[T], i: BackwardsIndex): var T {.inline.} = - ## Access the backwards indexed i-th element. + ## Accesses the backwards indexed `i`-th element and returns a mutable + ## reference to it. ## ## `deq[^1]` is the last element. runnableExamples: - var a = initDeque[int]() - for i in 1 .. 5: - a.addLast(10*i) - assert a[^1] == 50 - assert a[^4] == 20 - doAssertRaises(IndexDefect, echo a[^9]) + var a = [10, 20, 30, 40, 50].toDeque + inc(a[^1]) + assert a[^1] == 51 xBoundsCheck(deq, deq.len - int(i)) return deq[deq.len - int(i)] -proc `[]=`*[T](deq: var Deque[T], i: BackwardsIndex, x: T) {.inline.} = - ## Change the backwards indexed i-th element. +proc `[]=`*[T](deq: var Deque[T], i: BackwardsIndex, x: sink T) {.inline.} = + ## Sets the backwards indexed `i`-th element of `deq` to `x`. ## ## `deq[^1]` is the last element. runnableExamples: - var a = initDeque[int]() - for i in 1 .. 5: - a.addLast(10*i) + var a = [10, 20, 30, 40, 50].toDeque a[^1] = 99 a[^3] = 77 assert $a == "[10, 20, 77, 40, 99]" @@ -196,167 +188,143 @@ proc `[]=`*[T](deq: var Deque[T], i: BackwardsIndex, x: T) {.inline.} = xBoundsCheck(deq, deq.len - int(i)) deq[deq.len - int(i)] = x -iterator items*[T](deq: Deque[T]): T = - ## Yield every element of `deq`. - ## - ## **Examples:** - ## - ## .. code-block:: - ## var a = initDeque[int]() - ## for i in 1 .. 3: - ## a.addLast(10*i) - ## - ## for x in a: # the same as: for x in items(a): - ## echo x - ## - ## # 10 - ## # 20 - ## # 30 +iterator items*[T](deq: Deque[T]): lent T = + ## Yields every element of `deq`. ## - var i = deq.head - for c in 0 ..< deq.count: - yield deq.data[i] - i = (i + 1) and deq.mask + ## **See also:** + ## * `mitems iterator <#mitems.i,Deque[T]>`_ + runnableExamples: + from std/sequtils import toSeq + + let a = [10, 20, 30, 40, 50].toDeque + assert toSeq(a.items) == @[10, 20, 30, 40, 50] + + for c in 0 ..< deq.len: + yield deq.data[(deq.head + c.uint) and deq.mask] iterator mitems*[T](deq: var Deque[T]): var T = - ## Yield every element of `deq`, which can be modified. + ## Yields every element of `deq`, which can be modified. + ## + ## **See also:** + ## * `items iterator <#items.i,Deque[T]>`_ runnableExamples: - var a = initDeque[int]() - for i in 1 .. 5: - a.addLast(10*i) + var a = [10, 20, 30, 40, 50].toDeque assert $a == "[10, 20, 30, 40, 50]" for x in mitems(a): - x = 5*x - 1 + x = 5 * x - 1 assert $a == "[49, 99, 149, 199, 249]" - var i = deq.head - for c in 0 ..< deq.count: - yield deq.data[i] - i = (i + 1) and deq.mask + for c in 0 ..< deq.len: + yield deq.data[(deq.head + c.uint) and deq.mask] iterator pairs*[T](deq: Deque[T]): tuple[key: int, val: T] = - ## Yield every (position, value) of `deq`. - ## - ## **Examples:** - ## - ## .. code-block:: - ## var a = initDeque[int]() - ## for i in 1 .. 3: - ## a.addLast(10*i) - ## - ## for k, v in pairs(a): - ## echo "key: ", k, ", value: ", v - ## - ## # key: 0, value: 10 - ## # key: 1, value: 20 - ## # key: 2, value: 30 - ## - var i = deq.head - for c in 0 ..< deq.count: - yield (c, deq.data[i]) - i = (i + 1) and deq.mask + ## Yields every `(position, value)`-pair of `deq`. + runnableExamples: + from std/sequtils import toSeq + + let a = [10, 20, 30].toDeque + assert toSeq(a.pairs) == @[(0, 10), (1, 20), (2, 30)] + + for c in 0 ..< deq.len: + yield (c, deq.data[(deq.head + c.uint) and deq.mask]) proc contains*[T](deq: Deque[T], item: T): bool {.inline.} = - ## Return true if `item` is in `deq` or false if not found. - ## - ## Usually used via the ``in`` operator. - ## It is the equivalent of ``deq.find(item) >= 0``. + ## Returns true if `item` is in `deq` or false if not found. ## - ## .. code-block:: Nim - ## if x in q: - ## assert q.contains(x) + ## Usually used via the `in` operator. + ## It is the equivalent of `deq.find(item) >= 0`. + runnableExamples: + let q = [7, 9].toDeque + assert 7 in q + assert q.contains(7) + assert 8 notin q + for e in deq: if e == item: return true return false proc expandIfNeeded[T](deq: var Deque[T]) = checkIfInitialized(deq) - var cap = deq.mask + 1 - if unlikely(deq.count >= cap): + let cap = deq.data.len + assert deq.len <= cap + if unlikely(deq.len == cap): var n = newSeq[T](cap * 2) var i = 0 for x in mitems(deq): - when nimVM: n[i] = x # workaround for VM bug + when nimvm: n[i] = x # workaround for VM bug else: n[i] = move(x) inc i deq.data = move(n) - deq.mask = cap * 2 - 1 - deq.tail = deq.count + deq.tail = cap.uint deq.head = 0 -proc addFirst*[T](deq: var Deque[T], item: T) = - ## Add an `item` to the beginning of the `deq`. +proc addFirst*[T](deq: var Deque[T], item: sink T) = + ## Adds an `item` to the beginning of `deq`. ## - ## See also: - ## * `addLast proc <#addLast,Deque[T],T>`_ - ## * `peekFirst proc <#peekFirst,Deque[T]>`_ - ## * `peekLast proc <#peekLast,Deque[T]>`_ - ## * `popFirst proc <#popFirst,Deque[T]>`_ - ## * `popLast proc <#popLast,Deque[T]>`_ + ## **See also:** + ## * `addLast proc <#addLast,Deque[T],sinkT>`_ runnableExamples: var a = initDeque[int]() for i in 1 .. 5: - a.addFirst(10*i) + a.addFirst(10 * i) assert $a == "[50, 40, 30, 20, 10]" expandIfNeeded(deq) - inc deq.count - deq.head = (deq.head - 1) and deq.mask - deq.data[deq.head] = item + dec deq.head + deq.data[deq.head and deq.mask] = item -proc addLast*[T](deq: var Deque[T], item: T) = - ## Add an `item` to the end of the `deq`. +proc addLast*[T](deq: var Deque[T], item: sink T) = + ## Adds an `item` to the end of `deq`. ## - ## See also: - ## * `addFirst proc <#addFirst,Deque[T],T>`_ - ## * `peekFirst proc <#peekFirst,Deque[T]>`_ - ## * `peekLast proc <#peekLast,Deque[T]>`_ - ## * `popFirst proc <#popFirst,Deque[T]>`_ - ## * `popLast proc <#popLast,Deque[T]>`_ + ## **See also:** + ## * `addFirst proc <#addFirst,Deque[T],sinkT>`_ runnableExamples: var a = initDeque[int]() for i in 1 .. 5: - a.addLast(10*i) + a.addLast(10 * i) assert $a == "[10, 20, 30, 40, 50]" expandIfNeeded(deq) - inc deq.count - deq.data[deq.tail] = item - deq.tail = (deq.tail + 1) and deq.mask + deq.data[deq.tail and deq.mask] = item + inc deq.tail + +proc toDeque*[T](x: openArray[T]): Deque[T] {.since: (1, 3).} = + ## Creates a new deque that contains the elements of `x` (in the same order). + ## + ## **See also:** + ## * `initDeque proc <#initDeque,int>`_ + runnableExamples: + let a = toDeque([7, 8, 9]) + assert len(a) == 3 + assert $a == "[7, 8, 9]" -proc peekFirst*[T](deq: Deque[T]): T {.inline.} = + result.initImpl(x.len) + for item in items(x): + result.addLast(item) + +proc peekFirst*[T](deq: Deque[T]): lent T {.inline.} = ## Returns the first element of `deq`, but does not remove it from the deque. ## - ## See also: - ## * `addFirst proc <#addFirst,Deque[T],T>`_ - ## * `addLast proc <#addLast,Deque[T],T>`_ + ## **See also:** + ## * `peekFirst proc <#peekFirst,Deque[T]_2>`_ which returns a mutable reference ## * `peekLast proc <#peekLast,Deque[T]>`_ - ## * `popFirst proc <#popFirst,Deque[T]>`_ - ## * `popLast proc <#popLast,Deque[T]>`_ runnableExamples: - var a = initDeque[int]() - for i in 1 .. 5: - a.addLast(10*i) + let a = [10, 20, 30, 40, 50].toDeque assert $a == "[10, 20, 30, 40, 50]" assert a.peekFirst == 10 assert len(a) == 5 emptyCheck(deq) - result = deq.data[deq.head] + result = deq.data[deq.head and deq.mask] -proc peekLast*[T](deq: Deque[T]): T {.inline.} = +proc peekLast*[T](deq: Deque[T]): lent T {.inline.} = ## Returns the last element of `deq`, but does not remove it from the deque. ## - ## See also: - ## * `addFirst proc <#addFirst,Deque[T],T>`_ - ## * `addLast proc <#addLast,Deque[T],T>`_ + ## **See also:** + ## * `peekLast proc <#peekLast,Deque[T]_2>`_ which returns a mutable reference ## * `peekFirst proc <#peekFirst,Deque[T]>`_ - ## * `popFirst proc <#popFirst,Deque[T]>`_ - ## * `popLast proc <#popLast,Deque[T]>`_ runnableExamples: - var a = initDeque[int]() - for i in 1 .. 5: - a.addLast(10*i) + let a = [10, 20, 30, 40, 50].toDeque assert $a == "[10, 20, 30, 40, 50]" assert a.peekLast == 50 assert len(a) == 5 @@ -365,41 +333,31 @@ proc peekLast*[T](deq: Deque[T]): T {.inline.} = result = deq.data[(deq.tail - 1) and deq.mask] proc peekFirst*[T](deq: var Deque[T]): var T {.inline, since: (1, 3).} = - ## Returns the first element of `deq`, but does not remove it from the deque. + ## Returns a mutable reference to the first element of `deq`, + ## but does not remove it from the deque. ## - ## See also: - ## * `addFirst proc <#addFirst,Deque[T],T>`_ - ## * `addLast proc <#addLast,Deque[T],T>`_ - ## * `peekLast proc <#peekLast,Deque[T]>`_ - ## * `popFirst proc <#popFirst,Deque[T]>`_ - ## * `popLast proc <#popLast,Deque[T]>`_ + ## **See also:** + ## * `peekFirst proc <#peekFirst,Deque[T]>`_ + ## * `peekLast proc <#peekLast,Deque[T]_2>`_ runnableExamples: - var a = initDeque[int]() - for i in 1 .. 5: - a.addLast(10*i) - assert $a == "[10, 20, 30, 40, 50]" - assert a.peekFirst == 10 - assert len(a) == 5 + var a = [10, 20, 30, 40, 50].toDeque + a.peekFirst() = 99 + assert $a == "[99, 20, 30, 40, 50]" emptyCheck(deq) - result = deq.data[deq.head] + result = deq.data[deq.head and deq.mask] proc peekLast*[T](deq: var Deque[T]): var T {.inline, since: (1, 3).} = - ## Returns the last element of `deq`, but does not remove it from the deque. + ## Returns a mutable reference to the last element of `deq`, + ## but does not remove it from the deque. ## - ## See also: - ## * `addFirst proc <#addFirst,Deque[T],T>`_ - ## * `addLast proc <#addLast,Deque[T],T>`_ - ## * `peekFirst proc <#peekFirst,Deque[T]>`_ - ## * `popFirst proc <#popFirst,Deque[T]>`_ - ## * `popLast proc <#popLast,Deque[T]>`_ + ## **See also:** + ## * `peekFirst proc <#peekFirst,Deque[T]_2>`_ + ## * `peekLast proc <#peekLast,Deque[T]>`_ runnableExamples: - var a = initDeque[int]() - for i in 1 .. 5: - a.addLast(10*i) - assert $a == "[10, 20, 30, 40, 50]" - assert a.peekLast == 50 - assert len(a) == 5 + var a = [10, 20, 30, 40, 50].toDeque + a.peekLast() = 99 + assert $a == "[10, 20, 30, 40, 99]" emptyCheck(deq) result = deq.data[(deq.tail - 1) and deq.mask] @@ -408,218 +366,115 @@ template destroy(x: untyped) = reset(x) proc popFirst*[T](deq: var Deque[T]): T {.inline, discardable.} = - ## Remove and returns the first element of the `deq`. + ## Removes and returns the first element of the `deq`. ## ## See also: - ## * `addFirst proc <#addFirst,Deque[T],T>`_ - ## * `addLast proc <#addLast,Deque[T],T>`_ - ## * `peekFirst proc <#peekFirst,Deque[T]>`_ - ## * `peekLast proc <#peekLast,Deque[T]>`_ ## * `popLast proc <#popLast,Deque[T]>`_ - ## * `clear proc <#clear,Deque[T]>`_ ## * `shrink proc <#shrink,Deque[T],int,int>`_ runnableExamples: - var a = initDeque[int]() - for i in 1 .. 5: - a.addLast(10*i) + var a = [10, 20, 30, 40, 50].toDeque assert $a == "[10, 20, 30, 40, 50]" assert a.popFirst == 10 assert $a == "[20, 30, 40, 50]" emptyCheck(deq) - dec deq.count - result = deq.data[deq.head] - destroy(deq.data[deq.head]) - deq.head = (deq.head + 1) and deq.mask + result = move deq.data[deq.head and deq.mask] + inc deq.head proc popLast*[T](deq: var Deque[T]): T {.inline, discardable.} = - ## Remove and returns the last element of the `deq`. + ## Removes and returns the last element of the `deq`. ## - ## See also: - ## * `addFirst proc <#addFirst,Deque[T],T>`_ - ## * `addLast proc <#addLast,Deque[T],T>`_ - ## * `peekFirst proc <#peekFirst,Deque[T]>`_ - ## * `peekLast proc <#peekLast,Deque[T]>`_ + ## **See also:** ## * `popFirst proc <#popFirst,Deque[T]>`_ - ## * `clear proc <#clear,Deque[T]>`_ ## * `shrink proc <#shrink,Deque[T],int,int>`_ runnableExamples: - var a = initDeque[int]() - for i in 1 .. 5: - a.addLast(10*i) + var a = [10, 20, 30, 40, 50].toDeque assert $a == "[10, 20, 30, 40, 50]" assert a.popLast == 50 assert $a == "[10, 20, 30, 40]" emptyCheck(deq) - dec deq.count - deq.tail = (deq.tail - 1) and deq.mask - result = deq.data[deq.tail] - destroy(deq.data[deq.tail]) + dec deq.tail + result = move deq.data[deq.tail and deq.mask] proc clear*[T](deq: var Deque[T]) {.inline.} = ## Resets the deque so that it is empty. ## - ## See also: - ## * `clear proc <#clear,Deque[T]>`_ + ## **See also:** ## * `shrink proc <#shrink,Deque[T],int,int>`_ runnableExamples: - var a = initDeque[int]() - for i in 1 .. 5: - a.addFirst(10*i) - assert $a == "[50, 40, 30, 20, 10]" + var a = [10, 20, 30, 40, 50].toDeque + assert $a == "[10, 20, 30, 40, 50]" clear(a) assert len(a) == 0 for el in mitems(deq): destroy(el) - deq.count = 0 deq.tail = deq.head proc shrink*[T](deq: var Deque[T], fromFirst = 0, fromLast = 0) = - ## Remove `fromFirst` elements from the front of the deque and + ## Removes `fromFirst` elements from the front of the deque and ## `fromLast` elements from the back. ## ## If the supplied number of elements exceeds the total number of elements ## in the deque, the deque will remain empty. ## - ## See also: + ## **See also:** ## * `clear proc <#clear,Deque[T]>`_ + ## * `popFirst proc <#popFirst,Deque[T]>`_ + ## * `popLast proc <#popLast,Deque[T]>`_ runnableExamples: - var a = initDeque[int]() - for i in 1 .. 5: - a.addFirst(10*i) - assert $a == "[50, 40, 30, 20, 10]" + var a = [10, 20, 30, 40, 50].toDeque + assert $a == "[10, 20, 30, 40, 50]" a.shrink(fromFirst = 2, fromLast = 1) - assert $a == "[30, 20]" + assert $a == "[30, 40]" - if fromFirst + fromLast > deq.count: + if fromFirst + fromLast > deq.len: clear(deq) return for i in 0 ..< fromFirst: - destroy(deq.data[deq.head]) - deq.head = (deq.head + 1) and deq.mask + destroy(deq.data[deq.head and deq.mask]) + inc deq.head for i in 0 ..< fromLast: - destroy(deq.data[deq.tail]) - deq.tail = (deq.tail - 1) and deq.mask - - dec deq.count, fromFirst + fromLast + destroy(deq.data[(deq.tail - 1) and deq.mask]) + dec deq.tail proc `$`*[T](deq: Deque[T]): string = - ## Turn a deque into its string representation. + ## Turns a deque into its string representation. + runnableExamples: + let a = [10, 20, 30].toDeque + assert $a == "[10, 20, 30]" + result = "[" for x in deq: if result.len > 1: result.add(", ") result.addQuoted(x) result.add("]") +func `==`*[T](deq1, deq2: Deque[T]): bool = + ## The `==` operator for Deque. + ## Returns `true` if both deques contains the same values in the same order. + runnableExamples: + var a, b = initDeque[int]() + a.addFirst(2) + a.addFirst(1) + b.addLast(1) + b.addLast(2) + doAssert a == b + if deq1.len != deq2.len: + return false -when isMainModule: - var deq = initDeque[int](1) - deq.addLast(4) - deq.addFirst(9) - deq.addFirst(123) - var first = deq.popFirst() - deq.addLast(56) - assert(deq.peekLast() == 56) - deq.addLast(6) - assert(deq.peekLast() == 6) - var second = deq.popFirst() - deq.addLast(789) - assert(deq.peekLast() == 789) - - assert first == 123 - assert second == 9 - assert($deq == "[4, 56, 6, 789]") - - assert deq[0] == deq.peekFirst and deq.peekFirst == 4 - #assert deq[^1] == deq.peekLast and deq.peekLast == 789 - deq[0] = 42 - deq[deq.len - 1] = 7 - - assert 6 in deq and 789 notin deq - assert deq.find(6) >= 0 - assert deq.find(789) < 0 - - block: - var d = initDeque[int](1) - d.addLast 7 - d.addLast 8 - d.addLast 10 - d.addFirst 5 - d.addFirst 2 - d.addFirst 1 - d.addLast 20 - d.shrink(fromLast = 2) - doAssert($d == "[1, 2, 5, 7, 8]") - d.shrink(2, 1) - doAssert($d == "[5, 7]") - d.shrink(2, 2) - doAssert d.len == 0 - - for i in -2 .. 10: - if i in deq: - assert deq.contains(i) and deq.find(i) >= 0 - else: - assert(not deq.contains(i) and deq.find(i) < 0) + for i in 0 ..< deq1.len: + if deq1.data[(deq1.head + i.uint) and deq1.mask] != deq2.data[(deq2.head + i.uint) and deq2.mask]: + return false - when compileOption("boundChecks"): - try: - echo deq[99] - assert false - except IndexDefect: - discard - - try: - assert deq.len == 4 - for i in 0 ..< 5: deq.popFirst() - assert false - except IndexDefect: - discard - - # grabs some types of resize error. - deq = initDeque[int]() - for i in 1 .. 4: deq.addLast i - deq.popFirst() - deq.popLast() - for i in 5 .. 8: deq.addFirst i - assert $deq == "[8, 7, 6, 5, 2, 3]" - - # Similar to proc from the documentation example - proc foo(a, b: Positive) = # assume random positive values for `a` and `b`. - var deq = initDeque[int]() - assert deq.len == 0 - for i in 1 .. a: deq.addLast i - - if b < deq.len: # checking before indexed access. - assert deq[b] == b + 1 - - # The following two lines don't need any checking on access due to the logic - # of the program, but that would not be the case if `a` could be 0. - assert deq.peekFirst == 1 - assert deq.peekLast == a - - while deq.len > 0: # checking if the deque is empty - assert deq.popFirst() > 0 - - #foo(0,0) - foo(8, 5) - foo(10, 9) - foo(1, 1) - foo(2, 1) - foo(1, 5) - foo(3, 2) - - import sets - block t13310: - proc main() = - var q = initDeque[HashSet[int16]](2) - q.addFirst([1'i16].toHashSet) - q.addFirst([2'i16].toHashSet) - q.addFirst([3'i16].toHashSet) - assert $q == "[{3}, {2}, {1}]" - - static: - main() + true + +func hash*[T](deq: Deque[T]): Hash = + ## Hashing of Deque. + var h: Hash = 0 + for x in deq: + h = h !& hash(x) + !$h diff --git a/lib/pure/collections/hashcommon.nim b/lib/pure/collections/hashcommon.nim index e998145e7..17785c8c7 100644 --- a/lib/pure/collections/hashcommon.nim +++ b/lib/pure/collections/hashcommon.nim @@ -7,17 +7,17 @@ # distribution, for details about the copyright. # -# An ``include`` file which contains common code for +# An `include` file which contains common code for # hash sets and tables. +when defined(nimPreviewSlimSystem): + import std/assertions + +import std / outparams + const growthFactor = 2 -when not defined(nimHasDefault): - template default[T](t: typedesc[T]): T = - var v: T - v - # hcode for real keys cannot be zero. hcode==0 signifies an empty slot. These # two procs retain clarity of that encoding without the space cost of an enum. proc isEmpty(hcode: Hash): bool {.inline.} = @@ -30,16 +30,12 @@ proc nextTry(h, maxHash: Hash): Hash {.inline.} = result = (h + 1) and maxHash proc mustRehash[T](t: T): bool {.inline.} = + # If this is changed, make sure to synchronize it with `slotsNeeded` below assert(t.dataLen > t.counter) result = (t.dataLen * 2 < t.counter * 3) or (t.dataLen - t.counter < 4) -proc rightSize*(count: Natural): int {.inline.} = - ## Return the value of ``initialSize`` to support ``count`` items. - ## - ## If more items are expected to be added, simply add that - ## expected extra amount to the parameter before calling this. - # - # Make sure to synchronize with `mustRehash` +proc slotsNeeded(count: Natural): int {.inline.} = + # Make sure to synchronize with `mustRehash` above result = nextPowerOfTwo(count * 3 div 2 + 4) template rawGetKnownHCImpl() {.dirty.} = @@ -62,7 +58,10 @@ proc rawGetKnownHC[X, A](t: X, key: A, hc: Hash): int {.inline.} = template genHashImpl(key, hc: typed) = hc = hash(key) if hc == 0: # This almost never taken branch should be very predictable. - hc = 314159265 # Value doesn't matter; Any non-zero favorite is fine. + when sizeof(int) < 4: + hc = 31415 # Value doesn't matter; Any non-zero favorite is fine <= 16-bit. + else: + hc = 314159265 # Value doesn't matter; Any non-zero favorite is fine. template genHash(key: typed): Hash = var res: Hash @@ -73,5 +72,5 @@ template rawGetImpl() {.dirty.} = genHashImpl(key, hc) rawGetKnownHCImpl() -proc rawGet[X, A](t: X, key: A, hc: var Hash): int {.inline.} = +proc rawGet[X, A](t: X, key: A, hc: var Hash): int {.inline, outParamsAt: [3].} = rawGetImpl() diff --git a/lib/pure/collections/heapqueue.nim b/lib/pure/collections/heapqueue.nim index 608fd2920..96f9b4430 100644 --- a/lib/pure/collections/heapqueue.nim +++ b/lib/pure/collections/heapqueue.nim @@ -6,77 +6,91 @@ # See the file "copying.txt", included in this # distribution, for details about the copyright. -##[ - The `heapqueue` module implements a - `heap data structure<https://en.wikipedia.org/wiki/Heap_(data_structure)>`_ - that can be used as a - `priority queue<https://en.wikipedia.org/wiki/Priority_queue>`_. - Heaps are arrays for which `a[k] <= a[2*k+1]` and `a[k] <= a[2*k+2]` for - all `k`, counting elements from 0. The interesting property of a heap is that - `a[0]` is always its smallest element. - - Basic usage - ----------- - .. code-block:: Nim - import heapqueue - - var heap = initHeapQueue[int]() - heap.push(8) - heap.push(2) - heap.push(5) - # The first element is the lowest element - assert heap[0] == 2 - # Remove and return the lowest element - assert heap.pop() == 2 - # The lowest element remaining is 5 - assert heap[0] == 5 - - Usage with custom object - ------------------------ - To use a `HeapQueue` with a custom object, the `<` operator must be - implemented. - - .. code-block:: Nim - import heapqueue - - type Job = object - priority: int - proc `<`(a, b: Job): bool = a.priority < b.priority +## The `heapqueue` module implements a +## `binary heap data structure<https://en.wikipedia.org/wiki/Binary_heap>`_ +## that can be used as a `priority queue<https://en.wikipedia.org/wiki/Priority_queue>`_. +## They are represented as arrays for which `a[k] <= a[2*k+1]` and `a[k] <= a[2*k+2]` +## for all indices `k` (counting elements from 0). The interesting property of a heap is that +## `a[0]` is always its smallest element. +## +## Basic usage +## ----------- +## +runnableExamples: + var heap = [8, 2].toHeapQueue + heap.push(5) + # the first element is the lowest element + assert heap[0] == 2 + # remove and return the lowest element + assert heap.pop() == 2 + # the lowest element remaining is 5 + assert heap[0] == 5 + +## Usage with custom objects +## ------------------------- +## To use a `HeapQueue` with a custom object, the `<` operator must be +## implemented. + +runnableExamples: + type Job = object + priority: int + + proc `<`(a, b: Job): bool = a.priority < b.priority + + var jobs = initHeapQueue[Job]() + jobs.push(Job(priority: 1)) + jobs.push(Job(priority: 2)) + + assert jobs[0].priority == 1 - var jobs = initHeapQueue[Job]() - jobs.push(Job(priority: 1)) - jobs.push(Job(priority: 2)) - assert jobs[0].priority == 1 -]## import std/private/since +when defined(nimPreviewSlimSystem): + import std/assertions + type HeapQueue*[T] = object ## A heap queue, commonly known as a priority queue. data: seq[T] proc initHeapQueue*[T](): HeapQueue[T] = - ## Create a new empty heap. - discard + ## Creates a new empty heap. + ## + ## Heaps are initialized by default, so it is not necessary to call + ## this function explicitly. + ## + ## **See also:** + ## * `toHeapQueue proc <#toHeapQueue,openArray[T]>`_ + result = default(HeapQueue[T]) proc len*[T](heap: HeapQueue[T]): int {.inline.} = - ## Return the number of elements of `heap`. + ## Returns the number of elements of `heap`. + runnableExamples: + let heap = [9, 5, 8].toHeapQueue + assert heap.len == 3 + heap.data.len -proc `[]`*[T](heap: HeapQueue[T], i: Natural): T {.inline.} = - ## Access the i-th element of `heap`. +proc `[]`*[T](heap: HeapQueue[T], i: Natural): lent T {.inline.} = + ## Accesses the i-th element of `heap`. heap.data[i] -proc heapCmp[T](x, y: T): bool {.inline.} = - return (x < y) +iterator items*[T](heap: HeapQueue[T]): lent T {.inline, since: (2, 1, 1).} = + ## Iterates over each item of `heap`. + let L = len(heap) + for i in 0 .. high(heap.data): + yield heap.data[i] + assert(len(heap) == L, "the length of the HeapQueue changed while iterating over it") + +proc heapCmp[T](x, y: T): bool {.inline.} = x < y -proc siftdown[T](heap: var HeapQueue[T], startpos, p: int) = - ## 'heap' is a heap at all indices >= startpos, except possibly for pos. pos - ## is the index of a leaf with a possibly out-of-order value. Restore the +proc siftup[T](heap: var HeapQueue[T], startpos, p: int) = + ## `heap` is a heap at all indices >= `startpos`, except possibly for `p`. `p` + ## is the index of a leaf with a possibly out-of-order value. Restores the ## heap invariant. var pos = p - var newitem = heap[pos] + let newitem = heap[pos] # Follow the path to the root, moving parents down until finding a place # newitem fits. while pos > startpos: @@ -89,13 +103,14 @@ proc siftdown[T](heap: var HeapQueue[T], startpos, p: int) = break heap.data[pos] = newitem -proc siftup[T](heap: var HeapQueue[T], p: int) = +proc siftdownToBottom[T](heap: var HeapQueue[T], p: int) = + # This is faster when the element should be close to the bottom. let endpos = len(heap) var pos = p let startpos = pos let newitem = heap[pos] # Bubble up the smaller child until hitting a leaf. - var childpos = 2*pos + 1 # leftmost child position + var childpos = 2 * pos + 1 # leftmost child position while childpos < endpos: # Set childpos to index of smaller child. let rightpos = childpos + 1 @@ -104,132 +119,148 @@ proc siftup[T](heap: var HeapQueue[T], p: int) = # Move the smaller child up. heap.data[pos] = heap[childpos] pos = childpos - childpos = 2*pos + 1 - # The leaf at pos is empty now. Put newitem there, and bubble it up + childpos = 2 * pos + 1 + # The leaf at pos is empty now. Put newitem there, and bubble it up # to its final resting place (by sifting its parents down). heap.data[pos] = newitem - siftdown(heap, startpos, pos) + siftup(heap, startpos, pos) -proc push*[T](heap: var HeapQueue[T], item: T) = - ## Push `item` onto heap, maintaining the heap invariant. +proc siftdown[T](heap: var HeapQueue[T], p: int) = + let endpos = len(heap) + var pos = p + let newitem = heap[pos] + var childpos = 2 * pos + 1 + while childpos < endpos: + let rightpos = childpos + 1 + if rightpos < endpos and not heapCmp(heap[childpos], heap[rightpos]): + childpos = rightpos + if not heapCmp(heap[childpos], newitem): + break + heap.data[pos] = heap[childpos] + pos = childpos + childpos = 2 * pos + 1 + heap.data[pos] = newitem + +proc push*[T](heap: var HeapQueue[T], item: sink T) = + ## Pushes `item` onto `heap`, maintaining the heap invariant. heap.data.add(item) - siftdown(heap, 0, len(heap)-1) + siftup(heap, 0, len(heap) - 1) + +proc toHeapQueue*[T](x: openArray[T]): HeapQueue[T] {.since: (1, 3).} = + ## Creates a new HeapQueue that contains the elements of `x`. + ## + ## **See also:** + ## * `initHeapQueue proc <#initHeapQueue>`_ + runnableExamples: + var heap = [9, 5, 8].toHeapQueue + assert heap.pop() == 5 + assert heap[0] == 8 + + # see https://en.wikipedia.org/wiki/Binary_heap#Building_a_heap + result.data = @x + for i in countdown(x.len div 2 - 1, 0): + siftdown(result, i) proc pop*[T](heap: var HeapQueue[T]): T = - ## Pop and return the smallest item from `heap`, + ## Pops and returns the smallest item from `heap`, ## maintaining the heap invariant. + runnableExamples: + var heap = [9, 5, 8].toHeapQueue + assert heap.pop() == 5 + let lastelt = heap.data.pop() if heap.len > 0: result = heap[0] heap.data[0] = lastelt - siftup(heap, 0) + siftdownToBottom(heap, 0) else: result = lastelt proc find*[T](heap: HeapQueue[T], x: T): int {.since: (1, 3).} = - ## Linear scan to find index of item ``x`` or -1 if not found. + ## Linear scan to find the index of the item `x` or -1 if not found. + runnableExamples: + let heap = [9, 5, 8].toHeapQueue + assert heap.find(5) == 0 + assert heap.find(9) == 1 + assert heap.find(777) == -1 + result = -1 for i in 0 ..< heap.len: if heap[i] == x: return i +proc contains*[T](heap: HeapQueue[T], x: T): bool {.since: (2, 1, 1).} = + ## Returns true if `x` is in `heap` or false if not found. This is a shortcut + ## for `find(heap, x) >= 0`. + result = find(heap, x) >= 0 + proc del*[T](heap: var HeapQueue[T], index: Natural) = ## Removes the element at `index` from `heap`, maintaining the heap invariant. + runnableExamples: + var heap = [9, 5, 8].toHeapQueue + heap.del(1) + assert heap[0] == 5 + assert heap[1] == 8 + swap(heap.data[^1], heap.data[index]) let newLen = heap.len - 1 heap.data.setLen(newLen) if index < newLen: - heap.siftup(index) + siftdownToBottom(heap, index) -proc replace*[T](heap: var HeapQueue[T], item: T): T = - ## Pop and return the current smallest value, and add the new item. - ## This is more efficient than pop() followed by push(), and can be +proc replace*[T](heap: var HeapQueue[T], item: sink T): T = + ## Pops and returns the current smallest value, and add the new item. + ## This is more efficient than `pop()` followed by `push()`, and can be ## more appropriate when using a fixed-size heap. Note that the value - ## returned may be larger than item! That constrains reasonable uses of - ## this routine unless written as part of a conditional replacement: + ## returned may be larger than `item`! That constrains reasonable uses of + ## this routine unless written as part of a conditional replacement. ## - ## .. code-block:: nim - ## if item > heap[0]: - ## item = replace(heap, item) + ## **See also:** + ## * `pushpop proc <#pushpop,HeapQueue[T],sinkT>`_ + runnableExamples: + var heap = [5, 12].toHeapQueue + assert heap.replace(6) == 5 + assert heap.len == 2 + assert heap[0] == 6 + assert heap.replace(4) == 6 + result = heap[0] heap.data[0] = item - siftup(heap, 0) + siftdown(heap, 0) -proc pushpop*[T](heap: var HeapQueue[T], item: T): T = - ## Fast version of a push followed by a pop. - if heap.len > 0 and heapCmp(heap[0], item): - swap(item, heap[0]) - siftup(heap, 0) - return item +proc pushpop*[T](heap: var HeapQueue[T], item: sink T): T = + ## Fast version of a `push()` followed by a `pop()`. + ## + ## **See also:** + ## * `replace proc <#replace,HeapQueue[T],sinkT>`_ + runnableExamples: + var heap = [5, 12].toHeapQueue + assert heap.pushpop(6) == 5 + assert heap.len == 2 + assert heap[0] == 6 + assert heap.pushpop(4) == 4 + + result = item + if heap.len > 0 and heapCmp(heap.data[0], result): + swap(result, heap.data[0]) + siftdown(heap, 0) proc clear*[T](heap: var HeapQueue[T]) = - ## Remove all elements from `heap`, making it empty. + ## Removes all elements from `heap`, making it empty. runnableExamples: - var heap = initHeapQueue[int]() - heap.push(1) + var heap = [9, 5, 8].toHeapQueue heap.clear() assert heap.len == 0 + heap.data.setLen(0) proc `$`*[T](heap: HeapQueue[T]): string = - ## Turn a heap into its string representation. + ## Turns a heap into its string representation. runnableExamples: - var heap = initHeapQueue[int]() - heap.push(1) - heap.push(2) + let heap = [1, 2].toHeapQueue assert $heap == "[1, 2]" + result = "[" for x in heap.data: if result.len > 1: result.add(", ") result.addQuoted(x) result.add("]") - -when isMainModule: - proc toSortedSeq[T](h: HeapQueue[T]): seq[T] = - var tmp = h - result = @[] - while tmp.len > 0: - result.add(pop(tmp)) - - block: # Simple sanity test - var heap = initHeapQueue[int]() - let data = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0] - for item in data: - push(heap, item) - doAssert(heap[0] == 0) - doAssert(heap.toSortedSeq == @[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) - - block: # Test del - var heap = initHeapQueue[int]() - let data = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0] - for item in data: push(heap, item) - - heap.del(0) - doAssert(heap[0] == 1) - - heap.del(heap.find(7)) - doAssert(heap.toSortedSeq == @[1, 2, 3, 4, 5, 6, 8, 9]) - - heap.del(heap.find(5)) - doAssert(heap.toSortedSeq == @[1, 2, 3, 4, 6, 8, 9]) - - heap.del(heap.find(6)) - doAssert(heap.toSortedSeq == @[1, 2, 3, 4, 8, 9]) - - heap.del(heap.find(2)) - doAssert(heap.toSortedSeq == @[1, 3, 4, 8, 9]) - - doAssert(heap.find(2) == -1) - - block: # Test del last - var heap = initHeapQueue[int]() - let data = [1, 2, 3] - for item in data: push(heap, item) - - heap.del(2) - doAssert(heap.toSortedSeq == @[1, 2]) - - heap.del(1) - doAssert(heap.toSortedSeq == @[1]) - - heap.del(0) - doAssert(heap.toSortedSeq == @[]) diff --git a/lib/pure/collections/intsets.nim b/lib/pure/collections/intsets.nim index eae3fa447..765a23e97 100644 --- a/lib/pure/collections/intsets.nim +++ b/lib/pure/collections/intsets.nim @@ -7,676 +7,17 @@ # distribution, for details about the copyright. # -## The ``intsets`` module implements an efficient `int` set implemented as a -## `sparse bit set`:idx:. -## -## **Note**: Currently the assignment operator ``=`` for ``IntSet`` -## performs some rather meaningless shallow copy. Since Nim currently does -## not allow the assignment operator to be overloaded, use `assign proc -## <#assign,IntSet,IntSet>`_ to get a deep copy. -## -## **See also:** -## * `sets module <sets.html>`_ for more general hash sets +## Specialization of the generic `packedsets module <packedsets.html>`_ +## (see its documentation for more examples) for ordinal sparse sets. - -import - hashes - -type - BitScalar = uint - -const - InitIntSetSize = 8 # must be a power of two! - TrunkShift = 9 - BitsPerTrunk = 1 shl TrunkShift # needs to be a power of 2 and - # divisible by 64 - TrunkMask = BitsPerTrunk - 1 - IntsPerTrunk = BitsPerTrunk div (sizeof(BitScalar) * 8) - IntShift = 5 + ord(sizeof(BitScalar) == 8) # 5 or 6, depending on int width - IntMask = 1 shl IntShift - 1 +import std/private/since +import std/packedsets +export packedsets type - PTrunk = ref Trunk - Trunk = object - next: PTrunk # all nodes are connected with this pointer - key: int # start address at bit 0 - bits: array[0..IntsPerTrunk - 1, BitScalar] # a bit vector - - TrunkSeq = seq[PTrunk] - IntSet* = object ## An efficient set of `int` implemented as a sparse bit set. - elems: int # only valid for small numbers - counter, max: int - head: PTrunk - data: TrunkSeq - a: array[0..33, int] # profiling shows that 34 elements are enough - -proc mustRehash[T](t: T): bool {.inline.} = - let length = t.max + 1 - assert length > t.counter - result = (length * 2 < t.counter * 3) or (length - t.counter < 4) - -proc nextTry(h, maxHash: Hash, perturb: var Hash): Hash {.inline.} = - const PERTURB_SHIFT = 5 - var perturb2 = cast[uint](perturb) shr PERTURB_SHIFT - perturb = cast[Hash](perturb2) - result = ((5*h) + 1 + perturb) and maxHash - -proc intSetGet(t: IntSet, key: int): PTrunk = - var h = key and t.max - var perturb = key - while t.data[h] != nil: - if t.data[h].key == key: - return t.data[h] - h = nextTry(h, t.max, perturb) - result = nil - -proc intSetRawInsert(t: IntSet, data: var TrunkSeq, desc: PTrunk) = - var h = desc.key and t.max - var perturb = desc.key - while data[h] != nil: - assert(data[h] != desc) - h = nextTry(h, t.max, perturb) - assert(data[h] == nil) - data[h] = desc - -proc intSetEnlarge(t: var IntSet) = - var n: TrunkSeq - var oldMax = t.max - t.max = ((t.max + 1) * 2) - 1 - newSeq(n, t.max + 1) - for i in countup(0, oldMax): - if t.data[i] != nil: intSetRawInsert(t, n, t.data[i]) - swap(t.data, n) - -proc intSetPut(t: var IntSet, key: int): PTrunk = - var h = key and t.max - var perturb = key - while t.data[h] != nil: - if t.data[h].key == key: - return t.data[h] - h = nextTry(h, t.max, perturb) - if mustRehash(t): intSetEnlarge(t) - inc(t.counter) - h = key and t.max - perturb = key - while t.data[h] != nil: h = nextTry(h, t.max, perturb) - assert(t.data[h] == nil) - new(result) - result.next = t.head - result.key = key - t.head = result - t.data[h] = result - -proc bitincl(s: var IntSet, key: int) {.inline.} = - var ret: PTrunk - var t = intSetPut(s, `shr`(key, TrunkShift)) - var u = key and TrunkMask - t.bits[u shr IntShift] = t.bits[u shr IntShift] or - (BitScalar(1) shl (u and IntMask)) - -proc exclImpl(s: var IntSet, key: int) = - if s.elems <= s.a.len: - for i in 0..<s.elems: - if s.a[i] == key: - s.a[i] = s.a[s.elems-1] - dec s.elems - return - else: - var t = intSetGet(s, key shr TrunkShift) - if t != nil: - var u = key and TrunkMask - t.bits[u shr IntShift] = t.bits[u shr IntShift] and - not(BitScalar(1) shl (u and IntMask)) - -template dollarImpl(): untyped = - result = "{" - for key in items(s): - if result.len > 1: result.add(", ") - result.add($key) - result.add("}") - - -iterator items*(s: IntSet): int {.inline.} = - ## Iterates over any included element of `s`. - if s.elems <= s.a.len: - for i in 0..<s.elems: - yield s.a[i] - else: - var r = s.head - while r != nil: - var i = 0 - while i <= high(r.bits): - var w: uint = r.bits[i] - # taking a copy of r.bits[i] here is correct, because - # modifying operations are not allowed during traversation - var j = 0 - while w != 0: # test all remaining bits for zero - if (w and 1) != 0: # the bit is set! - yield (r.key shl TrunkShift) or (i shl IntShift +% j) - inc(j) - w = w shr 1 - inc(i) - r = r.next - - -proc initIntSet*: IntSet = - ## Returns an empty IntSet. - runnableExamples: - var a = initIntSet() - assert len(a) == 0 - - # newSeq(result.data, InitIntSetSize) - # result.max = InitIntSetSize-1 - result = IntSet( - elems: 0, - counter: 0, - max: 0, - head: nil, - data: when defined(nimNoNilSeqs): @[] else: nil) - # a: array[0..33, int] # profiling shows that 34 elements are enough - -proc contains*(s: IntSet, key: int): bool = - ## Returns true if `key` is in `s`. - ## - ## This allows the usage of `in` operator. - runnableExamples: - var a = initIntSet() - for x in [1, 3, 5]: - a.incl(x) - assert a.contains(3) - assert 3 in a - assert(not a.contains(8)) - assert 8 notin a - - if s.elems <= s.a.len: - for i in 0..<s.elems: - if s.a[i] == key: return true - else: - var t = intSetGet(s, `shr`(key, TrunkShift)) - if t != nil: - var u = key and TrunkMask - result = (t.bits[u shr IntShift] and - (BitScalar(1) shl (u and IntMask))) != 0 - else: - result = false - -proc incl*(s: var IntSet, key: int) = - ## Includes an element `key` in `s`. - ## - ## This doesn't do anything if `key` is already in `s`. - ## - ## See also: - ## * `excl proc <#excl,IntSet,int>`_ for excluding an element - ## * `incl proc <#incl,IntSet,IntSet>`_ for including other set - ## * `containsOrIncl proc <#containsOrIncl,IntSet,int>`_ - runnableExamples: - var a = initIntSet() - a.incl(3) - a.incl(3) - assert len(a) == 1 - - if s.elems <= s.a.len: - for i in 0..<s.elems: - if s.a[i] == key: return - if s.elems < s.a.len: - s.a[s.elems] = key - inc s.elems - return - newSeq(s.data, InitIntSetSize) - s.max = InitIntSetSize-1 - for i in 0..<s.elems: - bitincl(s, s.a[i]) - s.elems = s.a.len + 1 - # fall through: - bitincl(s, key) - -proc incl*(s: var IntSet, other: IntSet) = - ## Includes all elements from `other` into `s`. - ## - ## This is the in-place version of `s + other <#+,IntSet,IntSet>`_. - ## - ## See also: - ## * `excl proc <#excl,IntSet,IntSet>`_ for excluding other set - ## * `incl proc <#incl,IntSet,int>`_ for including an element - ## * `containsOrIncl proc <#containsOrIncl,IntSet,int>`_ - runnableExamples: - var - a = initIntSet() - b = initIntSet() - a.incl(1) - b.incl(5) - a.incl(b) - assert len(a) == 2 - assert 5 in a - - for item in other: incl(s, item) - -proc containsOrIncl*(s: var IntSet, key: int): bool = - ## Includes `key` in the set `s` and tells if `key` was already in `s`. - ## - ## The difference with regards to the `incl proc <#incl,IntSet,int>`_ is - ## that this proc returns `true` if `s` already contained `key`. The - ## proc will return `false` if `key` was added as a new value to `s` during - ## this call. - ## - ## See also: - ## * `incl proc <#incl,IntSet,int>`_ for including an element - ## * `missingOrExcl proc <#missingOrExcl,IntSet,int>`_ - runnableExamples: - var a = initIntSet() - assert a.containsOrIncl(3) == false - assert a.containsOrIncl(3) == true - assert a.containsOrIncl(4) == false - - if s.elems <= s.a.len: - for i in 0..<s.elems: - if s.a[i] == key: - return true - incl(s, key) - result = false - else: - var t = intSetGet(s, `shr`(key, TrunkShift)) - if t != nil: - var u = key and TrunkMask - result = (t.bits[u shr IntShift] and BitScalar(1) shl (u and IntMask)) != 0 - if not result: - t.bits[u shr IntShift] = t.bits[u shr IntShift] or - (BitScalar(1) shl (u and IntMask)) - else: - incl(s, key) - result = false - -proc excl*(s: var IntSet, key: int) = - ## Excludes `key` from the set `s`. - ## - ## This doesn't do anything if `key` is not found in `s`. - ## - ## See also: - ## * `incl proc <#incl,IntSet,int>`_ for including an element - ## * `excl proc <#excl,IntSet,IntSet>`_ for excluding other set - ## * `missingOrExcl proc <#missingOrExcl,IntSet,int>`_ - runnableExamples: - var a = initIntSet() - a.incl(3) - a.excl(3) - a.excl(3) - a.excl(99) - assert len(a) == 0 - exclImpl(s, key) - -proc excl*(s: var IntSet, other: IntSet) = - ## Excludes all elements from `other` from `s`. - ## - ## This is the in-place version of `s - other <#-,IntSet,IntSet>`_. - ## - ## See also: - ## * `incl proc <#incl,IntSet,IntSet>`_ for including other set - ## * `excl proc <#excl,IntSet,int>`_ for excluding an element - ## * `missingOrExcl proc <#missingOrExcl,IntSet,int>`_ - runnableExamples: - var - a = initIntSet() - b = initIntSet() - a.incl(1) - a.incl(5) - b.incl(5) - a.excl(b) - assert len(a) == 1 - assert 5 notin a - - for item in other: excl(s, item) - -proc len*(s: IntSet): int {.inline.} = - ## Returns the number of elements in `s`. - if s.elems < s.a.len: - result = s.elems - else: - result = 0 - for _ in s: - inc(result) - -proc missingOrExcl*(s: var IntSet, key: int): bool = - ## Excludes `key` in the set `s` and tells if `key` was already missing from `s`. - ## - ## The difference with regards to the `excl proc <#excl,IntSet,int>`_ is - ## that this proc returns `true` if `key` was missing from `s`. - ## The proc will return `false` if `key` was in `s` and it was removed - ## during this call. - ## - ## See also: - ## * `excl proc <#excl,IntSet,int>`_ for excluding an element - ## * `excl proc <#excl,IntSet,IntSet>`_ for excluding other set - ## * `containsOrIncl proc <#containsOrIncl,IntSet,int>`_ - runnableExamples: - var a = initIntSet() - a.incl(5) - assert a.missingOrExcl(5) == false - assert a.missingOrExcl(5) == true - - var count = s.len - exclImpl(s, key) - result = count == s.len - -proc clear*(result: var IntSet) = - ## Clears the IntSet back to an empty state. - runnableExamples: - var a = initIntSet() - a.incl(5) - a.incl(7) - clear(a) - assert len(a) == 0 - - # setLen(result.data, InitIntSetSize) - # for i in 0..InitIntSetSize-1: result.data[i] = nil - # result.max = InitIntSetSize-1 - when defined(nimNoNilSeqs): - result.data = @[] - else: - result.data = nil - result.max = 0 - result.counter = 0 - result.head = nil - result.elems = 0 - -proc isNil*(x: IntSet): bool {.inline.} = x.head.isNil and x.elems == 0 - -proc assign*(dest: var IntSet, src: IntSet) = - ## Copies `src` to `dest`. - ## `dest` does not need to be initialized by `initIntSet proc <#initIntSet>`_. - runnableExamples: - var - a = initIntSet() - b = initIntSet() - b.incl(5) - b.incl(7) - a.assign(b) - assert len(a) == 2 - - if src.elems <= src.a.len: - when defined(nimNoNilSeqs): - dest.data = @[] - else: - dest.data = nil - dest.max = 0 - dest.counter = src.counter - dest.head = nil - dest.elems = src.elems - dest.a = src.a - else: - dest.counter = src.counter - dest.max = src.max - dest.elems = src.elems - newSeq(dest.data, src.data.len) - - var it = src.head - while it != nil: - var h = it.key and dest.max - var perturb = it.key - while dest.data[h] != nil: h = nextTry(h, dest.max, perturb) - assert(dest.data[h] == nil) - var n: PTrunk - new(n) - n.next = dest.head - n.key = it.key - n.bits = it.bits - dest.head = n - dest.data[h] = n - it = it.next - -proc union*(s1, s2: IntSet): IntSet = - ## Returns the union of the sets `s1` and `s2`. - ## - ## The same as `s1 + s2 <#+,IntSet,IntSet>`_. - runnableExamples: - var - a = initIntSet() - b = initIntSet() - a.incl(1); a.incl(2); a.incl(3) - b.incl(3); b.incl(4); b.incl(5) - assert union(a, b).len == 5 - ## {1, 2, 3, 4, 5} - - result.assign(s1) - incl(result, s2) - -proc intersection*(s1, s2: IntSet): IntSet = - ## Returns the intersection of the sets `s1` and `s2`. - ## - ## The same as `s1 * s2 <#*,IntSet,IntSet>`_. - runnableExamples: - var - a = initIntSet() - b = initIntSet() - a.incl(1); a.incl(2); a.incl(3) - b.incl(3); b.incl(4); b.incl(5) - assert intersection(a, b).len == 1 - ## {3} - - result = initIntSet() - for item in s1: - if contains(s2, item): - incl(result, item) - -proc difference*(s1, s2: IntSet): IntSet = - ## Returns the difference of the sets `s1` and `s2`. - ## - ## The same as `s1 - s2 <#-,IntSet,IntSet>`_. - runnableExamples: - var - a = initIntSet() - b = initIntSet() - a.incl(1); a.incl(2); a.incl(3) - b.incl(3); b.incl(4); b.incl(5) - assert difference(a, b).len == 2 - ## {1, 2} - - result = initIntSet() - for item in s1: - if not contains(s2, item): - incl(result, item) - -proc symmetricDifference*(s1, s2: IntSet): IntSet = - ## Returns the symmetric difference of the sets `s1` and `s2`. - runnableExamples: - var - a = initIntSet() - b = initIntSet() - a.incl(1); a.incl(2); a.incl(3) - b.incl(3); b.incl(4); b.incl(5) - assert symmetricDifference(a, b).len == 4 - ## {1, 2, 4, 5} - - result.assign(s1) - for item in s2: - if containsOrIncl(result, item): excl(result, item) - -proc `+`*(s1, s2: IntSet): IntSet {.inline.} = - ## Alias for `union(s1, s2) <#union,IntSet,IntSet>`_. - result = union(s1, s2) - -proc `*`*(s1, s2: IntSet): IntSet {.inline.} = - ## Alias for `intersection(s1, s2) <#intersection,IntSet,IntSet>`_. - result = intersection(s1, s2) - -proc `-`*(s1, s2: IntSet): IntSet {.inline.} = - ## Alias for `difference(s1, s2) <#difference,IntSet,IntSet>`_. - result = difference(s1, s2) - -proc disjoint*(s1, s2: IntSet): bool = - ## Returns true if the sets `s1` and `s2` have no items in common. - runnableExamples: - var - a = initIntSet() - b = initIntSet() - a.incl(1); a.incl(2) - b.incl(2); b.incl(3) - assert disjoint(a, b) == false - b.excl(2) - assert disjoint(a, b) == true - - for item in s1: - if contains(s2, item): - return false - return true - -proc card*(s: IntSet): int {.inline.} = - ## Alias for `len() <#len,IntSet>`_. - result = s.len() - -proc `<=`*(s1, s2: IntSet): bool = - ## Returns true if `s1` is subset of `s2`. - ## - ## A subset `s1` has all of its elements in `s2`, and `s2` doesn't necessarily - ## have more elements than `s1`. That is, `s1` can be equal to `s2`. - runnableExamples: - var - a = initIntSet() - b = initIntSet() - a.incl(1) - b.incl(1); b.incl(2) - assert a <= b - a.incl(2) - assert a <= b - a.incl(3) - assert(not (a <= b)) - - for item in s1: - if not s2.contains(item): - return false - return true - -proc `<`*(s1, s2: IntSet): bool = - ## Returns true if `s1` is proper subset of `s2`. - ## - ## A strict or proper subset `s1` has all of its elements in `s2`, but `s2` has - ## more elements than `s1`. - runnableExamples: - var - a = initIntSet() - b = initIntSet() - a.incl(1) - b.incl(1); b.incl(2) - assert a < b - a.incl(2) - assert(not (a < b)) - return s1 <= s2 and not (s2 <= s1) - -proc `==`*(s1, s2: IntSet): bool = - ## Returns true if both `s1` and `s2` have the same elements and set size. - return s1 <= s2 and s2 <= s1 - -proc `$`*(s: IntSet): string = - ## The `$` operator for int sets. - ## - ## Converts the set `s` to a string, mostly for logging and printing purposes. - dollarImpl() - - - -when isMainModule: - import sequtils, algorithm - - var x = initIntSet() - x.incl(1) - x.incl(2) - x.incl(7) - x.incl(1056) - - x.incl(1044) - x.excl(1044) - - assert x.containsOrIncl(888) == false - assert 888 in x - assert x.containsOrIncl(888) == true - - assert x.missingOrExcl(888) == false - assert 888 notin x - assert x.missingOrExcl(888) == true - - var xs = toSeq(items(x)) - xs.sort(cmp[int]) - assert xs == @[1, 2, 7, 1056] - - var y: IntSet - assign(y, x) - var ys = toSeq(items(y)) - ys.sort(cmp[int]) - assert ys == @[1, 2, 7, 1056] - - assert x == y - - var z: IntSet - for i in 0..1000: - incl z, i - assert z.len() == i+1 - for i in 0..1000: - assert z.contains(i) - - var w = initIntSet() - w.incl(1) - w.incl(4) - w.incl(50) - w.incl(1001) - w.incl(1056) - - var xuw = x.union(w) - var xuws = toSeq(items(xuw)) - xuws.sort(cmp[int]) - assert xuws == @[1, 2, 4, 7, 50, 1001, 1056] - - var xiw = x.intersection(w) - var xiws = toSeq(items(xiw)) - xiws.sort(cmp[int]) - assert xiws == @[1, 1056] - - var xdw = x.difference(w) - var xdws = toSeq(items(xdw)) - xdws.sort(cmp[int]) - assert xdws == @[2, 7] - - var xsw = x.symmetricDifference(w) - var xsws = toSeq(items(xsw)) - xsws.sort(cmp[int]) - assert xsws == @[2, 4, 7, 50, 1001] - - x.incl(w) - xs = toSeq(items(x)) - xs.sort(cmp[int]) - assert xs == @[1, 2, 4, 7, 50, 1001, 1056] - - assert w <= x - - assert w < x - - assert(not disjoint(w, x)) - - var u = initIntSet() - u.incl(3) - u.incl(5) - u.incl(500) - assert disjoint(u, x) - - var v = initIntSet() - v.incl(2) - v.incl(50) - - x.excl(v) - xs = toSeq(items(x)) - xs.sort(cmp[int]) - assert xs == @[1, 4, 7, 1001, 1056] - - proc bug12366 = - var - x = initIntSet() - y = initIntSet() - n = 3584 + IntSet* = PackedSet[int] - for i in 0..n: - x.incl(i) - y.incl(i) +proc toIntSet*(x: openArray[int]): IntSet {.since: (1, 3), inline.} = toPackedSet[int](x) - let z = symmetricDifference(x, y) - doAssert z.len == 0 - doAssert $z == "{}" +proc initIntSet*(): IntSet {.inline.} = initPackedSet[int]() - bug12366() diff --git a/lib/pure/collections/lists.nim b/lib/pure/collections/lists.nim index bd22949bf..6b88747ef 100644 --- a/lib/pure/collections/lists.nim +++ b/lib/pure/collections/lists.nim @@ -13,119 +13,90 @@ ## * `singly linked rings <#SinglyLinkedRing>`_ (circular lists) ## * `doubly linked rings <#DoublyLinkedRing>`_ (circular lists) ## -## -## Basic Usage -## =========== -## +## # Basic Usage ## Because it makes no sense to do otherwise, the `next` and `prev` pointers ## are not hidden from you and can be manipulated directly for efficiency. ## -## Lists -## ----- -## -## .. code-block:: -## import lists -## -## var -## l = initDoublyLinkedList[int]() -## a = newDoublyLinkedNode[int](3) -## b = newDoublyLinkedNode[int](7) -## c = newDoublyLinkedNode[int](9) -## -## l.append(a) -## l.append(b) -## l.prepend(c) -## -## assert a.next == b -## assert a.prev == c -## assert c.next == a -## assert c.next.next == b -## assert c.prev == nil -## assert b.next == nil -## -## -## Rings -## ----- -## -## .. code-block:: -## import lists -## -## var -## l = initSinglyLinkedRing[int]() -## a = newSinglyLinkedNode[int](3) -## b = newSinglyLinkedNode[int](7) -## c = newSinglyLinkedNode[int](9) -## -## l.append(a) -## l.append(b) -## l.prepend(c) -## -## assert c.next == a -## assert a.next == b -## assert c.next.next == b -## assert b.next == c -## assert c.next.next.next == c -## -## See also -## ======== -## +## ## Lists +runnableExamples: + var list = initDoublyLinkedList[int]() + let + a = newDoublyLinkedNode[int](3) + b = newDoublyLinkedNode[int](7) + c = newDoublyLinkedNode[int](9) + + list.add(a) + list.add(b) + list.prepend(c) + + assert a.next == b + assert a.prev == c + assert c.next == a + assert c.next.next == b + assert c.prev == nil + assert b.next == nil + +## ## Rings +runnableExamples: + var ring = initSinglyLinkedRing[int]() + let + a = newSinglyLinkedNode[int](3) + b = newSinglyLinkedNode[int](7) + c = newSinglyLinkedNode[int](9) + + ring.add(a) + ring.add(b) + ring.prepend(c) + + assert c.next == a + assert a.next == b + assert c.next.next == b + assert b.next == c + assert c.next.next.next == c + +## # See also ## * `deques module <deques.html>`_ for double-ended queues -## * `sharedlist module <sharedlist.html>`_ for shared singly-linked lists - -when not defined(nimhygiene): - {.pragma: dirty.} +import std/private/since -when not defined(nimHasCursor): - {.pragma: cursor.} +when defined(nimPreviewSlimSystem): + import std/assertions type - DoublyLinkedNodeObj*[T] = object ## \ - ## A node a doubly linked list consists of. + DoublyLinkedNodeObj*[T] = object + ## A node of a doubly linked list. ## ## It consists of a `value` field, and pointers to `next` and `prev`. - next*: <//>(ref DoublyLinkedNodeObj[T]) - prev* {.cursor.}: ref DoublyLinkedNodeObj[T] + next*: DoublyLinkedNode[T] + prev* {.cursor.}: DoublyLinkedNode[T] value*: T DoublyLinkedNode*[T] = ref DoublyLinkedNodeObj[T] - SinglyLinkedNodeObj*[T] = object ## \ - ## A node a singly linked list consists of. + SinglyLinkedNodeObj*[T] = object + ## A node of a singly linked list. ## ## It consists of a `value` field, and a pointer to `next`. - next*: <//>(ref SinglyLinkedNodeObj[T]) + next*: SinglyLinkedNode[T] value*: T SinglyLinkedNode*[T] = ref SinglyLinkedNodeObj[T] - SinglyLinkedList*[T] = object ## \ + SinglyLinkedList*[T] = object ## A singly linked list. - ## - ## Use `initSinglyLinkedList proc <#initSinglyLinkedList>`_ to create - ## a new empty list. - head*: <//>(SinglyLinkedNode[T]) + head*: SinglyLinkedNode[T] tail* {.cursor.}: SinglyLinkedNode[T] - DoublyLinkedList*[T] = object ## \ + DoublyLinkedList*[T] = object ## A doubly linked list. - ## - ## Use `initDoublyLinkedList proc <#initDoublyLinkedList>`_ to create - ## a new empty list. - head*: <//>(DoublyLinkedNode[T]) + head*: DoublyLinkedNode[T] tail* {.cursor.}: DoublyLinkedNode[T] - SinglyLinkedRing*[T] = object ## \ + SinglyLinkedRing*[T] = object ## A singly linked ring. - ## - ## Use `initSinglyLinkedRing proc <#initSinglyLinkedRing>`_ to create - ## a new empty ring. - head*: <//>(SinglyLinkedNode[T]) + head*: SinglyLinkedNode[T] tail* {.cursor.}: SinglyLinkedNode[T] - DoublyLinkedRing*[T] = object ## \ + DoublyLinkedRing*[T] = object ## A doubly linked ring. - ## - ## Use `initDoublyLinkedRing proc <#initDoublyLinkedRing>`_ to create - ## a new empty ring. head*: DoublyLinkedNode[T] SomeLinkedList*[T] = SinglyLinkedList[T] | DoublyLinkedList[T] @@ -138,54 +109,70 @@ type proc initSinglyLinkedList*[T](): SinglyLinkedList[T] = ## Creates a new singly linked list that is empty. + ## + ## Singly linked lists are initialized by default, so it is not necessary to + ## call this function explicitly. runnableExamples: - var a = initSinglyLinkedList[int]() + let a = initSinglyLinkedList[int]() + discard proc initDoublyLinkedList*[T](): DoublyLinkedList[T] = ## Creates a new doubly linked list that is empty. + ## + ## Doubly linked lists are initialized by default, so it is not necessary to + ## call this function explicitly. runnableExamples: - var a = initDoublyLinkedList[int]() + let a = initDoublyLinkedList[int]() + discard proc initSinglyLinkedRing*[T](): SinglyLinkedRing[T] = ## Creates a new singly linked ring that is empty. + ## + ## Singly linked rings are initialized by default, so it is not necessary to + ## call this function explicitly. runnableExamples: - var a = initSinglyLinkedRing[int]() + let a = initSinglyLinkedRing[int]() + discard proc initDoublyLinkedRing*[T](): DoublyLinkedRing[T] = ## Creates a new doubly linked ring that is empty. + ## + ## Doubly linked rings are initialized by default, so it is not necessary to + ## call this function explicitly. runnableExamples: - var a = initDoublyLinkedRing[int]() + let a = initDoublyLinkedRing[int]() + discard -proc newDoublyLinkedNode*[T](value: T): <//>(DoublyLinkedNode[T]) = +proc newDoublyLinkedNode*[T](value: T): DoublyLinkedNode[T] = ## Creates a new doubly linked node with the given `value`. runnableExamples: - var n = newDoublyLinkedNode[int](5) + let n = newDoublyLinkedNode[int](5) assert n.value == 5 new(result) result.value = value -proc newSinglyLinkedNode*[T](value: T): <//>(SinglyLinkedNode[T]) = +proc newSinglyLinkedNode*[T](value: T): SinglyLinkedNode[T] = ## Creates a new singly linked node with the given `value`. runnableExamples: - var n = newSinglyLinkedNode[int](5) + let n = newSinglyLinkedNode[int](5) assert n.value == 5 new(result) result.value = value template itemsListImpl() {.dirty.} = - var it = L.head + var it {.cursor.} = L.head while it != nil: yield it.value it = it.next template itemsRingImpl() {.dirty.} = - var it = L.head + var it {.cursor.} = L.head if it != nil: while true: yield it.value @@ -195,101 +182,91 @@ template itemsRingImpl() {.dirty.} = iterator items*[T](L: SomeLinkedList[T]): T = ## Yields every value of `L`. ## - ## See also: + ## **See also:** ## * `mitems iterator <#mitems.i,SomeLinkedList[T]>`_ ## * `nodes iterator <#nodes.i,SomeLinkedList[T]>`_ - ## - ## **Examples:** - ## - ## .. code-block:: - ## var a = initSinglyLinkedList[int]() - ## for i in 1 .. 3: - ## a.append(10*i) - ## - ## for x in a: # the same as: for x in items(a): - ## echo x - ## - ## # 10 - ## # 20 - ## # 30 + runnableExamples: + from std/sugar import collect + from std/sequtils import toSeq + let a = collect(initSinglyLinkedList): + for i in 1..3: 10 * i + assert toSeq(items(a)) == toSeq(a) + assert toSeq(a) == @[10, 20, 30] + itemsListImpl() iterator items*[T](L: SomeLinkedRing[T]): T = ## Yields every value of `L`. ## - ## See also: + ## **See also:** ## * `mitems iterator <#mitems.i,SomeLinkedRing[T]>`_ ## * `nodes iterator <#nodes.i,SomeLinkedRing[T]>`_ - ## - ## **Examples:** - ## - ## .. code-block:: - ## var a = initSinglyLinkedRing[int]() - ## for i in 1 .. 3: - ## a.append(10*i) - ## - ## for x in a: # the same as: for x in items(a): - ## echo x - ## - ## # 10 - ## # 20 - ## # 30 + runnableExamples: + from std/sugar import collect + from std/sequtils import toSeq + let a = collect(initSinglyLinkedRing): + for i in 1..3: 10 * i + assert toSeq(items(a)) == toSeq(a) + assert toSeq(a) == @[10, 20, 30] + itemsRingImpl() iterator mitems*[T](L: var SomeLinkedList[T]): var T = ## Yields every value of `L` so that you can modify it. ## - ## See also: + ## **See also:** ## * `items iterator <#items.i,SomeLinkedList[T]>`_ ## * `nodes iterator <#nodes.i,SomeLinkedList[T]>`_ runnableExamples: var a = initSinglyLinkedList[int]() - for i in 1 .. 5: - a.append(10*i) + for i in 1..5: + a.add(10 * i) assert $a == "[10, 20, 30, 40, 50]" for x in mitems(a): - x = 5*x - 1 + x = 5 * x - 1 assert $a == "[49, 99, 149, 199, 249]" + itemsListImpl() iterator mitems*[T](L: var SomeLinkedRing[T]): var T = ## Yields every value of `L` so that you can modify it. ## - ## See also: + ## **See also:** ## * `items iterator <#items.i,SomeLinkedRing[T]>`_ ## * `nodes iterator <#nodes.i,SomeLinkedRing[T]>`_ runnableExamples: var a = initSinglyLinkedRing[int]() - for i in 1 .. 5: - a.append(10*i) + for i in 1..5: + a.add(10 * i) assert $a == "[10, 20, 30, 40, 50]" for x in mitems(a): - x = 5*x - 1 + x = 5 * x - 1 assert $a == "[49, 99, 149, 199, 249]" + itemsRingImpl() iterator nodes*[T](L: SomeLinkedList[T]): SomeLinkedNode[T] = ## Iterates over every node of `x`. Removing the current node from the ## list during traversal is supported. ## - ## See also: + ## **See also:** ## * `items iterator <#items.i,SomeLinkedList[T]>`_ ## * `mitems iterator <#mitems.i,SomeLinkedList[T]>`_ runnableExamples: var a = initDoublyLinkedList[int]() - for i in 1 .. 5: - a.append(10*i) + for i in 1..5: + a.add(10 * i) assert $a == "[10, 20, 30, 40, 50]" for x in nodes(a): if x.value == 30: a.remove(x) else: - x.value = 5*x.value - 1 + x.value = 5 * x.value - 1 assert $a == "[49, 99, 199, 249]" - var it = L.head + var it {.cursor.} = L.head while it != nil: - var nxt = it.next + let nxt = it.next yield it it = nxt @@ -297,31 +274,35 @@ iterator nodes*[T](L: SomeLinkedRing[T]): SomeLinkedNode[T] = ## Iterates over every node of `x`. Removing the current node from the ## list during traversal is supported. ## - ## See also: + ## **See also:** ## * `items iterator <#items.i,SomeLinkedRing[T]>`_ ## * `mitems iterator <#mitems.i,SomeLinkedRing[T]>`_ runnableExamples: var a = initDoublyLinkedRing[int]() - for i in 1 .. 5: - a.append(10*i) + for i in 1..5: + a.add(10 * i) assert $a == "[10, 20, 30, 40, 50]" for x in nodes(a): if x.value == 30: a.remove(x) else: - x.value = 5*x.value - 1 + x.value = 5 * x.value - 1 assert $a == "[49, 99, 199, 249]" - var it = L.head + var it {.cursor.} = L.head if it != nil: while true: - var nxt = it.next + let nxt = it.next yield it it = nxt if it == L.head: break proc `$`*[T](L: SomeLinkedCollection[T]): string = ## Turns a list into its string representation for logging and printing. + runnableExamples: + let a = [1, 2, 3, 4].toSinglyLinkedList + assert $a == "[1, 2, 3, 4]" + result = "[" for x in nodes(L): if result.len > 1: result.add(", ") @@ -332,12 +313,10 @@ proc find*[T](L: SomeLinkedCollection[T], value: T): SomeLinkedNode[T] = ## Searches in the list for a value. Returns `nil` if the value does not ## exist. ## - ## See also: + ## **See also:** ## * `contains proc <#contains,SomeLinkedCollection[T],T>`_ runnableExamples: - var a = initSinglyLinkedList[int]() - a.append(9) - a.append(8) + let a = [9, 8].toSinglyLinkedList assert a.find(9).value == 9 assert a.find(1) == nil @@ -346,14 +325,13 @@ proc find*[T](L: SomeLinkedCollection[T], value: T): SomeLinkedNode[T] = proc contains*[T](L: SomeLinkedCollection[T], value: T): bool {.inline.} = ## Searches in the list for a value. Returns `false` if the value does not - ## exist, `true` otherwise. + ## exist, `true` otherwise. This allows the usage of the `in` and `notin` + ## operators. ## - ## See also: + ## **See also:** ## * `find proc <#find,SomeLinkedCollection[T],T>`_ runnableExamples: - var a = initSinglyLinkedList[int]() - a.append(9) - a.append(8) + let a = [9, 8].toSinglyLinkedList assert a.contains(9) assert 8 in a assert(not a.contains(1)) @@ -361,20 +339,65 @@ proc contains*[T](L: SomeLinkedCollection[T], value: T): bool {.inline.} = result = find(L, value) != nil -proc append*[T](L: var SinglyLinkedList[T], - n: SinglyLinkedNode[T]) {.inline.} = +proc prepend*[T: SomeLinkedList](a: var T, b: T) {.since: (1, 5, 1).} = + ## Prepends a shallow copy of `b` to the beginning of `a`. + ## + ## **See also:** + ## * `prependMoved proc <#prependMoved,T,T>`_ + ## for moving the second list instead of copying + runnableExamples: + from std/sequtils import toSeq + var a = [4, 5].toSinglyLinkedList + let b = [1, 2, 3].toSinglyLinkedList + a.prepend(b) + assert a.toSeq == [1, 2, 3, 4, 5] + assert b.toSeq == [1, 2, 3] + a.prepend(a) + assert a.toSeq == [1, 2, 3, 4, 5, 1, 2, 3, 4, 5] + + var tmp = b.copy + tmp.addMoved(a) + a = tmp + +proc prependMoved*[T: SomeLinkedList](a, b: var T) {.since: (1, 5, 1).} = + ## Moves `b` before the head of `a`. Efficiency: O(1). + ## Note that `b` becomes empty after the operation unless it has the same address as `a`. + ## Self-prepending results in a cycle. + ## + ## **See also:** + ## * `prepend proc <#prepend,T,T>`_ + ## for prepending a copy of a list + runnableExamples: + import std/[sequtils, enumerate, sugar] + var + a = [4, 5].toSinglyLinkedList + b = [1, 2, 3].toSinglyLinkedList + c = [0, 1].toSinglyLinkedList + a.prependMoved(b) + assert a.toSeq == [1, 2, 3, 4, 5] + assert b.toSeq == [] + c.prependMoved(c) + let s = collect: + for i, ci in enumerate(c): + if i == 6: break + ci + assert s == [0, 1, 0, 1, 0, 1] + + b.addMoved(a) + swap a, b + +proc add*[T](L: var SinglyLinkedList[T], n: SinglyLinkedNode[T]) {.inline.} = ## Appends (adds to the end) a node `n` to `L`. Efficiency: O(1). ## - ## See also: - ## * `append proc <#append,SinglyLinkedList[T],T>`_ for appending a value + ## **See also:** + ## * `add proc <#add,SinglyLinkedList[T],T>`_ for appending a value ## * `prepend proc <#prepend,SinglyLinkedList[T],SinglyLinkedNode[T]>`_ ## for prepending a node ## * `prepend proc <#prepend,SinglyLinkedList[T],T>`_ for prepending a value runnableExamples: - var - a = initSinglyLinkedList[int]() - n = newSinglyLinkedNode[int](9) - a.append(n) + var a = initSinglyLinkedList[int]() + let n = newSinglyLinkedNode[int](9) + a.add(n) assert a.contains(9) n.next = nil @@ -384,34 +407,34 @@ proc append*[T](L: var SinglyLinkedList[T], L.tail = n if L.head == nil: L.head = n -proc append*[T](L: var SinglyLinkedList[T], value: T) {.inline.} = +proc add*[T](L: var SinglyLinkedList[T], value: T) {.inline.} = ## Appends (adds to the end) a value to `L`. Efficiency: O(1). ## - ## See also: - ## * `append proc <#append,SinglyLinkedList[T],T>`_ for appending a value + ## **See also:** + ## * `add proc <#add,SinglyLinkedList[T],T>`_ for appending a value ## * `prepend proc <#prepend,SinglyLinkedList[T],SinglyLinkedNode[T]>`_ ## for prepending a node ## * `prepend proc <#prepend,SinglyLinkedList[T],T>`_ for prepending a value runnableExamples: var a = initSinglyLinkedList[int]() - a.append(9) - a.append(8) + a.add(9) + a.add(8) assert a.contains(9) - append(L, newSinglyLinkedNode(value)) + + add(L, newSinglyLinkedNode(value)) proc prepend*[T](L: var SinglyLinkedList[T], n: SinglyLinkedNode[T]) {.inline.} = ## Prepends (adds to the beginning) a node to `L`. Efficiency: O(1). ## - ## See also: - ## * `append proc <#append,SinglyLinkedList[T],SinglyLinkedNode[T]>`_ + ## **See also:** + ## * `add proc <#add,SinglyLinkedList[T],SinglyLinkedNode[T]>`_ ## for appending a node - ## * `append proc <#append,SinglyLinkedList[T],T>`_ for appending a value + ## * `add proc <#add,SinglyLinkedList[T],T>`_ for appending a value ## * `prepend proc <#prepend,SinglyLinkedList[T],T>`_ for prepending a value runnableExamples: - var - a = initSinglyLinkedList[int]() - n = newSinglyLinkedNode[int](9) + var a = initSinglyLinkedList[int]() + let n = newSinglyLinkedNode[int](9) a.prepend(n) assert a.contains(9) @@ -422,10 +445,10 @@ proc prepend*[T](L: var SinglyLinkedList[T], proc prepend*[T](L: var SinglyLinkedList[T], value: T) {.inline.} = ## Prepends (adds to the beginning) a node to `L`. Efficiency: O(1). ## - ## See also: - ## * `append proc <#append,SinglyLinkedList[T],SinglyLinkedNode[T]>`_ + ## **See also:** + ## * `add proc <#add,SinglyLinkedList[T],SinglyLinkedNode[T]>`_ ## for appending a node - ## * `append proc <#append,SinglyLinkedList[T],T>`_ for appending a value + ## * `add proc <#add,SinglyLinkedList[T],T>`_ for appending a value ## * `prepend proc <#prepend,SinglyLinkedList[T],SinglyLinkedNode[T]>`_ ## for prepending a node runnableExamples: @@ -433,25 +456,80 @@ proc prepend*[T](L: var SinglyLinkedList[T], value: T) {.inline.} = a.prepend(9) a.prepend(8) assert a.contains(9) - prepend(L, newSinglyLinkedNode(value)) + prepend(L, newSinglyLinkedNode(value)) +func copy*[T](a: SinglyLinkedList[T]): SinglyLinkedList[T] {.since: (1, 5, 1).} = + ## Creates a shallow copy of `a`. + runnableExamples: + from std/sequtils import toSeq + type Foo = ref object + x: int + var + f = Foo(x: 1) + a = [f].toSinglyLinkedList + let b = a.copy + a.add([f].toSinglyLinkedList) + assert a.toSeq == [f, f] + assert b.toSeq == [f] # b isn't modified... + f.x = 42 + assert a.head.value.x == 42 + assert b.head.value.x == 42 # ... but the elements are not deep copied + + let c = [1, 2, 3].toSinglyLinkedList + assert $c == $c.copy + + result = initSinglyLinkedList[T]() + for x in a.items: + result.add(x) + +proc addMoved*[T](a, b: var SinglyLinkedList[T]) {.since: (1, 5, 1).} = + ## Moves `b` to the end of `a`. Efficiency: O(1). + ## Note that `b` becomes empty after the operation unless it has the same address as `a`. + ## Self-adding results in a cycle. + ## + ## **See also:** + ## * `add proc <#add,T,T>`_ for adding a copy of a list + runnableExamples: + import std/[sequtils, enumerate, sugar] + var + a = [1, 2, 3].toSinglyLinkedList + b = [4, 5].toSinglyLinkedList + c = [0, 1].toSinglyLinkedList + a.addMoved(b) + assert a.toSeq == [1, 2, 3, 4, 5] + assert b.toSeq == [] + c.addMoved(c) + let s = collect: + for i, ci in enumerate(c): + if i == 6: break + ci + assert s == [0, 1, 0, 1, 0, 1] + + if b.head != nil: + if a.head == nil: + a.head = b.head + else: + a.tail.next = b.head + a.tail = b.tail + if a.addr != b.addr: + b.head = nil + b.tail = nil -proc append*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) = +proc add*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) = ## Appends (adds to the end) a node `n` to `L`. Efficiency: O(1). ## - ## See also: - ## * `append proc <#append,DoublyLinkedList[T],T>`_ for appending a value + ## **See also:** + ## * `add proc <#add,DoublyLinkedList[T],T>`_ for appending a value ## * `prepend proc <#prepend,DoublyLinkedList[T],DoublyLinkedNode[T]>`_ ## for prepending a node ## * `prepend proc <#prepend,DoublyLinkedList[T],T>`_ for prepending a value ## * `remove proc <#remove,DoublyLinkedList[T],DoublyLinkedNode[T]>`_ ## for removing a node runnableExamples: - var - a = initDoublyLinkedList[int]() - n = newDoublyLinkedNode[int](9) - a.append(n) + var a = initDoublyLinkedList[int]() + let n = newDoublyLinkedNode[int](9) + a.add(n) assert a.contains(9) n.next = nil @@ -462,11 +540,11 @@ proc append*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) = L.tail = n if L.head == nil: L.head = n -proc append*[T](L: var DoublyLinkedList[T], value: T) = +proc add*[T](L: var DoublyLinkedList[T], value: T) = ## Appends (adds to the end) a value to `L`. Efficiency: O(1). ## - ## See also: - ## * `append proc <#append,DoublyLinkedList[T],DoublyLinkedNode[T]>`_ + ## **See also:** + ## * `add proc <#add,DoublyLinkedList[T],DoublyLinkedNode[T]>`_ ## for appending a node ## * `prepend proc <#prepend,DoublyLinkedList[T],DoublyLinkedNode[T]>`_ ## for prepending a node @@ -475,25 +553,25 @@ proc append*[T](L: var DoublyLinkedList[T], value: T) = ## for removing a node runnableExamples: var a = initDoublyLinkedList[int]() - a.append(9) - a.append(8) + a.add(9) + a.add(8) assert a.contains(9) - append(L, newDoublyLinkedNode(value)) + + add(L, newDoublyLinkedNode(value)) proc prepend*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) = ## Prepends (adds to the beginning) a node `n` to `L`. Efficiency: O(1). ## - ## See also: - ## * `append proc <#append,DoublyLinkedList[T],DoublyLinkedNode[T]>`_ + ## **See also:** + ## * `add proc <#add,DoublyLinkedList[T],DoublyLinkedNode[T]>`_ ## for appending a node - ## * `append proc <#append,DoublyLinkedList[T],T>`_ for appending a value + ## * `add proc <#add,DoublyLinkedList[T],T>`_ for appending a value ## * `prepend proc <#prepend,DoublyLinkedList[T],T>`_ for prepending a value ## * `remove proc <#remove,DoublyLinkedList[T],DoublyLinkedNode[T]>`_ ## for removing a node runnableExamples: - var - a = initDoublyLinkedList[int]() - n = newDoublyLinkedNode[int](9) + var a = initDoublyLinkedList[int]() + let n = newDoublyLinkedNode[int](9) a.prepend(n) assert a.contains(9) @@ -508,10 +586,10 @@ proc prepend*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) = proc prepend*[T](L: var DoublyLinkedList[T], value: T) = ## Prepends (adds to the beginning) a value to `L`. Efficiency: O(1). ## - ## See also: - ## * `append proc <#append,DoublyLinkedList[T],DoublyLinkedNode[T]>`_ + ## **See also:** + ## * `add proc <#add,DoublyLinkedList[T],DoublyLinkedNode[T]>`_ ## for appending a node - ## * `append proc <#append,DoublyLinkedList[T],T>`_ for appending a value + ## * `add proc <#add,DoublyLinkedList[T],T>`_ for appending a value ## * `prepend proc <#prepend,DoublyLinkedList[T],DoublyLinkedNode[T]>`_ ## for prepending a node ## * `remove proc <#remove,DoublyLinkedList[T],DoublyLinkedNode[T]>`_ @@ -521,18 +599,147 @@ proc prepend*[T](L: var DoublyLinkedList[T], value: T) = a.prepend(9) a.prepend(8) assert a.contains(9) + prepend(L, newDoublyLinkedNode(value)) +func copy*[T](a: DoublyLinkedList[T]): DoublyLinkedList[T] {.since: (1, 5, 1).} = + ## Creates a shallow copy of `a`. + runnableExamples: + from std/sequtils import toSeq + type Foo = ref object + x: int + var + f = Foo(x: 1) + a = [f].toDoublyLinkedList + let b = a.copy + a.add([f].toDoublyLinkedList) + assert a.toSeq == [f, f] + assert b.toSeq == [f] # b isn't modified... + f.x = 42 + assert a.head.value.x == 42 + assert b.head.value.x == 42 # ... but the elements are not deep copied + + let c = [1, 2, 3].toDoublyLinkedList + assert $c == $c.copy + + result = initDoublyLinkedList[T]() + for x in a.items: + result.add(x) + +proc addMoved*[T](a, b: var DoublyLinkedList[T]) {.since: (1, 5, 1).} = + ## Moves `b` to the end of `a`. Efficiency: O(1). + ## Note that `b` becomes empty after the operation unless it has the same address as `a`. + ## Self-adding results in a cycle. + ## + ## **See also:** + ## * `add proc <#add,T,T>`_ + ## for adding a copy of a list + runnableExamples: + import std/[sequtils, enumerate, sugar] + var + a = [1, 2, 3].toDoublyLinkedList + b = [4, 5].toDoublyLinkedList + c = [0, 1].toDoublyLinkedList + a.addMoved(b) + assert a.toSeq == [1, 2, 3, 4, 5] + assert b.toSeq == [] + c.addMoved(c) + let s = collect: + for i, ci in enumerate(c): + if i == 6: break + ci + assert s == [0, 1, 0, 1, 0, 1] + + if b.head != nil: + if a.head == nil: + a.head = b.head + else: + b.head.prev = a.tail + a.tail.next = b.head + a.tail = b.tail + if a.addr != b.addr: + b.head = nil + b.tail = nil + +proc add*[T: SomeLinkedList](a: var T, b: T) {.since: (1, 5, 1).} = + ## Appends a shallow copy of `b` to the end of `a`. + ## + ## **See also:** + ## * `addMoved proc <#addMoved,SinglyLinkedList[T],SinglyLinkedList[T]>`_ + ## * `addMoved proc <#addMoved,DoublyLinkedList[T],DoublyLinkedList[T]>`_ + ## for moving the second list instead of copying + runnableExamples: + from std/sequtils import toSeq + var a = [1, 2, 3].toSinglyLinkedList + let b = [4, 5].toSinglyLinkedList + a.add(b) + assert a.toSeq == [1, 2, 3, 4, 5] + assert b.toSeq == [4, 5] + a.add(a) + assert a.toSeq == [1, 2, 3, 4, 5, 1, 2, 3, 4, 5] + + var tmp = b.copy + a.addMoved(tmp) + +proc remove*[T](L: var SinglyLinkedList[T], n: SinglyLinkedNode[T]): bool {.discardable.} = + ## Removes a node `n` from `L`. + ## Returns `true` if `n` was found in `L`. + ## Efficiency: O(n); the list is traversed until `n` is found. + ## Attempting to remove an element not contained in the list is a no-op. + ## When the list is cyclic, the cycle is preserved after removal. + runnableExamples: + import std/[sequtils, enumerate, sugar] + var a = [0, 1, 2].toSinglyLinkedList + let n = a.head.next + assert n.value == 1 + assert a.remove(n) == true + assert a.toSeq == [0, 2] + assert a.remove(n) == false + assert a.toSeq == [0, 2] + a.addMoved(a) # cycle: [0, 2, 0, 2, ...] + a.remove(a.head) + let s = collect: + for i, ai in enumerate(a): + if i == 4: break + ai + assert s == [2, 2, 2, 2] + + if n == L.head: + L.head = n.next + if L.tail.next == n: + L.tail.next = L.head # restore cycle + else: + var prev {.cursor.} = L.head + while prev.next != n and prev.next != nil: + prev = prev.next + if prev.next == nil: + return false + prev.next = n.next + if L.tail == n: + L.tail = prev # update tail if we removed the last node + true + proc remove*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) = ## Removes a node `n` from `L`. Efficiency: O(1). + ## This function assumes, for the sake of efficiency, that `n` is contained in `L`, + ## otherwise the effects are undefined. + ## When the list is cyclic, the cycle is preserved after removal. runnableExamples: - var - a = initDoublyLinkedList[int]() - n = newDoublyLinkedNode[int](5) - a.append(n) - assert 5 in a + import std/[sequtils, enumerate, sugar] + var a = [0, 1, 2].toSinglyLinkedList + let n = a.head.next + assert n.value == 1 a.remove(n) - assert 5 notin a + assert a.toSeq == [0, 2] + a.remove(n) + assert a.toSeq == [0, 2] + a.addMoved(a) # cycle: [0, 2, 0, 2, ...] + a.remove(a.head) + let s = collect: + for i, ai in enumerate(a): + if i == 4: break + ai + assert s == [2, 2, 2, 2] if n == L.tail: L.tail = n.prev if n == L.head: L.head = n.next @@ -541,59 +748,57 @@ proc remove*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) = -proc append*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) = +proc add*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) = ## Appends (adds to the end) a node `n` to `L`. Efficiency: O(1). ## - ## See also: - ## * `append proc <#append,SinglyLinkedRing[T],T>`_ for appending a value + ## **See also:** + ## * `add proc <#add,SinglyLinkedRing[T],T>`_ for appending a value ## * `prepend proc <#prepend,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_ ## for prepending a node ## * `prepend proc <#prepend,SinglyLinkedRing[T],T>`_ for prepending a value runnableExamples: - var - a = initSinglyLinkedRing[int]() - n = newSinglyLinkedNode[int](9) - a.append(n) + var a = initSinglyLinkedRing[int]() + let n = newSinglyLinkedNode[int](9) + a.add(n) assert a.contains(9) if L.head != nil: n.next = L.head assert(L.tail != nil) L.tail.next = n - L.tail = n else: n.next = n L.head = n - L.tail = n + L.tail = n -proc append*[T](L: var SinglyLinkedRing[T], value: T) = +proc add*[T](L: var SinglyLinkedRing[T], value: T) = ## Appends (adds to the end) a value to `L`. Efficiency: O(1). ## - ## See also: - ## * `append proc <#append,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_ + ## **See also:** + ## * `add proc <#add,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_ ## for appending a node ## * `prepend proc <#prepend,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_ ## for prepending a node ## * `prepend proc <#prepend,SinglyLinkedRing[T],T>`_ for prepending a value runnableExamples: var a = initSinglyLinkedRing[int]() - a.append(9) - a.append(8) + a.add(9) + a.add(8) assert a.contains(9) - append(L, newSinglyLinkedNode(value)) + + add(L, newSinglyLinkedNode(value)) proc prepend*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) = ## Prepends (adds to the beginning) a node `n` to `L`. Efficiency: O(1). ## - ## See also: - ## * `append proc <#append,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_ + ## **See also:** + ## * `add proc <#add,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_ ## for appending a node - ## * `append proc <#append,SinglyLinkedRing[T],T>`_ for appending a value + ## * `add proc <#add,SinglyLinkedRing[T],T>`_ for appending a value ## * `prepend proc <#prepend,SinglyLinkedRing[T],T>`_ for prepending a value runnableExamples: - var - a = initSinglyLinkedRing[int]() - n = newSinglyLinkedNode[int](9) + var a = initSinglyLinkedRing[int]() + let n = newSinglyLinkedNode[int](9) a.prepend(n) assert a.contains(9) @@ -609,10 +814,10 @@ proc prepend*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) = proc prepend*[T](L: var SinglyLinkedRing[T], value: T) = ## Prepends (adds to the beginning) a value to `L`. Efficiency: O(1). ## - ## See also: - ## * `append proc <#append,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_ + ## **See also:** + ## * `add proc <#add,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_ ## for appending a node - ## * `append proc <#append,SinglyLinkedRing[T],T>`_ for appending a value + ## * `add proc <#add,SinglyLinkedRing[T],T>`_ for appending a value ## * `prepend proc <#prepend,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_ ## for prepending a node runnableExamples: @@ -620,25 +825,25 @@ proc prepend*[T](L: var SinglyLinkedRing[T], value: T) = a.prepend(9) a.prepend(8) assert a.contains(9) + prepend(L, newSinglyLinkedNode(value)) -proc append*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) = +proc add*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) = ## Appends (adds to the end) a node `n` to `L`. Efficiency: O(1). ## - ## See also: - ## * `append proc <#append,DoublyLinkedRing[T],T>`_ for appending a value + ## **See also:** + ## * `add proc <#add,DoublyLinkedRing[T],T>`_ for appending a value ## * `prepend proc <#prepend,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_ ## for prepending a node ## * `prepend proc <#prepend,DoublyLinkedRing[T],T>`_ for prepending a value ## * `remove proc <#remove,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_ ## for removing a node runnableExamples: - var - a = initDoublyLinkedRing[int]() - n = newDoublyLinkedNode[int](9) - a.append(n) + var a = initDoublyLinkedRing[int]() + let n = newDoublyLinkedNode[int](9) + a.add(n) assert a.contains(9) if L.head != nil: @@ -651,11 +856,11 @@ proc append*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) = n.next = n L.head = n -proc append*[T](L: var DoublyLinkedRing[T], value: T) = +proc add*[T](L: var DoublyLinkedRing[T], value: T) = ## Appends (adds to the end) a value to `L`. Efficiency: O(1). ## - ## See also: - ## * `append proc <#append,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_ + ## **See also:** + ## * `add proc <#add,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_ ## for appending a node ## * `prepend proc <#prepend,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_ ## for prepending a node @@ -664,25 +869,25 @@ proc append*[T](L: var DoublyLinkedRing[T], value: T) = ## for removing a node runnableExamples: var a = initDoublyLinkedRing[int]() - a.append(9) - a.append(8) + a.add(9) + a.add(8) assert a.contains(9) - append(L, newDoublyLinkedNode(value)) + + add(L, newDoublyLinkedNode(value)) proc prepend*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) = ## Prepends (adds to the beginning) a node `n` to `L`. Efficiency: O(1). ## - ## See also: - ## * `append proc <#append,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_ + ## **See also:** + ## * `add proc <#add,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_ ## for appending a node - ## * `append proc <#append,DoublyLinkedRing[T],T>`_ for appending a value + ## * `add proc <#add,DoublyLinkedRing[T],T>`_ for appending a value ## * `prepend proc <#prepend,DoublyLinkedRing[T],T>`_ for prepending a value ## * `remove proc <#remove,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_ ## for removing a node runnableExamples: - var - a = initDoublyLinkedRing[int]() - n = newDoublyLinkedNode[int](9) + var a = initDoublyLinkedRing[int]() + let n = newDoublyLinkedNode[int](9) a.prepend(n) assert a.contains(9) @@ -699,10 +904,10 @@ proc prepend*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) = proc prepend*[T](L: var DoublyLinkedRing[T], value: T) = ## Prepends (adds to the beginning) a value to `L`. Efficiency: O(1). ## - ## See also: - ## * `append proc <#append,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_ + ## **See also:** + ## * `add proc <#add,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_ ## for appending a node - ## * `append proc <#append,DoublyLinkedRing[T],T>`_ for appending a value + ## * `add proc <#add,DoublyLinkedRing[T],T>`_ for appending a value ## * `prepend proc <#prepend,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_ ## for prepending a node ## * `remove proc <#remove,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_ @@ -712,15 +917,17 @@ proc prepend*[T](L: var DoublyLinkedRing[T], value: T) = a.prepend(9) a.prepend(8) assert a.contains(9) + prepend(L, newDoublyLinkedNode(value)) proc remove*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) = ## Removes `n` from `L`. Efficiency: O(1). + ## This function assumes, for the sake of efficiency, that `n` is contained in `L`, + ## otherwise the effects are undefined. runnableExamples: - var - a = initDoublyLinkedRing[int]() - n = newDoublyLinkedNode[int](5) - a.append(n) + var a = initDoublyLinkedRing[int]() + let n = newDoublyLinkedNode[int](5) + a.add(n) assert 5 in a a.remove(n) assert 5 notin a @@ -728,9 +935,81 @@ proc remove*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) = n.next.prev = n.prev n.prev.next = n.next if n == L.head: - var p = L.head.prev + let p = L.head.prev if p == L.head: # only one element left: L.head = nil else: - L.head = L.head.prev + L.head = p + +proc append*[T](a: var (SinglyLinkedList[T] | SinglyLinkedRing[T]), + b: SinglyLinkedList[T] | SinglyLinkedNode[T] | T) = + ## Alias for `a.add(b)`. + ## + ## **See also:** + ## * `add proc <#add,SinglyLinkedList[T],SinglyLinkedNode[T]>`_ + ## * `add proc <#add,SinglyLinkedList[T],T>`_ + ## * `add proc <#add,T,T>`_ + a.add(b) + +proc append*[T](a: var (DoublyLinkedList[T] | DoublyLinkedRing[T]), + b: DoublyLinkedList[T] | DoublyLinkedNode[T] | T) = + ## Alias for `a.add(b)`. + ## + ## **See also:** + ## * `add proc <#add,DoublyLinkedList[T],DoublyLinkedNode[T]>`_ + ## * `add proc <#add,DoublyLinkedList[T],T>`_ + ## * `add proc <#add,T,T>`_ + a.add(b) + +proc appendMoved*[T: SomeLinkedList](a, b: var T) {.since: (1, 5, 1).} = + ## Alias for `a.addMoved(b)`. + ## + ## **See also:** + ## * `addMoved proc <#addMoved,SinglyLinkedList[T],SinglyLinkedList[T]>`_ + ## * `addMoved proc <#addMoved,DoublyLinkedList[T],DoublyLinkedList[T]>`_ + a.addMoved(b) + +func toSinglyLinkedList*[T](elems: openArray[T]): SinglyLinkedList[T] {.since: (1, 5, 1).} = + ## Creates a new `SinglyLinkedList` from the members of `elems`. + runnableExamples: + from std/sequtils import toSeq + let a = [1, 2, 3, 4, 5].toSinglyLinkedList + assert a.toSeq == [1, 2, 3, 4, 5] + + result = initSinglyLinkedList[T]() + for elem in elems.items: + result.add(elem) + +func toSinglyLinkedRing*[T](elems: openArray[T]): SinglyLinkedRing[T] = + ## Creates a new `SinglyLinkedRing` from the members of `elems`. + runnableExamples: + from std/sequtils import toSeq + let a = [1, 2, 3, 4, 5].toSinglyLinkedRing + assert a.toSeq == [1, 2, 3, 4, 5] + + result = initSinglyLinkedRing[T]() + for elem in elems.items: + result.add(elem) + +func toDoublyLinkedList*[T](elems: openArray[T]): DoublyLinkedList[T] {.since: (1, 5, 1).} = + ## Creates a new `DoublyLinkedList` from the members of `elems`. + runnableExamples: + from std/sequtils import toSeq + let a = [1, 2, 3, 4, 5].toDoublyLinkedList + assert a.toSeq == [1, 2, 3, 4, 5] + + result = initDoublyLinkedList[T]() + for elem in elems.items: + result.add(elem) + +func toDoublyLinkedRing*[T](elems: openArray[T]): DoublyLinkedRing[T] = + ## Creates a new `DoublyLinkedRing` from the members of `elems`. + runnableExamples: + from std/sequtils import toSeq + let a = [1, 2, 3, 4, 5].toDoublyLinkedRing + assert a.toSeq == [1, 2, 3, 4, 5] + + result = initDoublyLinkedRing[T]() + for elem in elems.items: + result.add(elem) diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index f101d508e..3c0d8dc0e 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -7,19 +7,19 @@ # distribution, for details about the copyright. # -## Although this module has ``seq`` in its name, it implements operations -## not only for `seq`:idx: type, but for three built-in container types under -## the ``openArray`` umbrella: +## Although this module has `seq` in its name, it implements operations +## not only for the `seq`:idx: type, but for three built-in container types +## under the `openArray` umbrella: ## * sequences ## * strings ## * array ## -## The system module defines several common functions, such as: -## * ``newSeq[T]`` for creating new sequences of type ``T`` -## * ``@`` for converting arrays and strings to sequences -## * ``add`` for adding new elements to strings and sequences -## * ``&`` for string and seq concatenation -## * ``in`` (alias for ``contains``) and ``notin`` for checking if an item is +## The `system` module defines several common functions, such as: +## * `newSeq[T]` for creating new sequences of type `T` +## * `@` for converting arrays and strings to sequences +## * `add` for adding new elements to strings and sequences +## * `&` for string and seq concatenation +## * `in` (alias for `contains`) and `notin` for checking if an item is ## in a container ## ## This module builds upon that, providing additional functionality in form of @@ -27,45 +27,52 @@ ## languages. ## ## For functional style programming you have different options at your disposal: -## * pass `anonymous proc<manual.html#procedures-anonymous-procs>`_ -## * import `sugar module<sugar.html>`_ and use -## `=> macro<sugar.html#%3D>.m,untyped,untyped>`_ +## * the `sugar.collect macro<sugar.html#collect.m%2Cuntyped%2Cuntyped>`_ +## * pass an `anonymous proc<manual.html#procedures-anonymous-procs>`_ +## * import the `sugar module<sugar.html>`_ and use +## the `=> macro<sugar.html#%3D>.m,untyped,untyped>`_ ## * use `...It templates<#18>`_ ## (`mapIt<#mapIt.t,typed,untyped>`_, ## `filterIt<#filterIt.t,untyped,untyped>`_, etc.) ## -## The chaining of functions is possible thanks to the +## Chaining of functions is possible thanks to the ## `method call syntax<manual.html#procedures-method-call-syntax>`_. -## -## .. code-block:: -## import sequtils, sugar -## -## # Creating a sequence from 1 to 10, multiplying each member by 2, -## # keeping only the members which are not divisible by 6. -## let -## foo = toSeq(1..10).map(x => x*2).filter(x => x mod 6 != 0) -## bar = toSeq(1..10).mapIt(it*2).filterIt(it mod 6 != 0) -## -## doAssert foo == bar -## echo foo # @[2, 4, 8, 10, 14, 16, 20] -## -## echo foo.any(x => x > 17) # true -## echo bar.allIt(it < 20) # false -## echo foo.foldl(a + b) # 74; sum of all members -## -## .. code-block:: -## import sequtils -## from strutils import join -## -## let -## vowels = @"aeiou" # creates a sequence @['a', 'e', 'i', 'o', 'u'] -## foo = "sequtils is an awesome module" -## -## echo foo.filterIt(it notin vowels).join # "sqtls s n wsm mdl" -## -## ---- -## -## **See also**: + +runnableExamples: + import std/sugar + + # Creating a sequence from 1 to 10, multiplying each member by 2, + # keeping only the members which are not divisible by 6. + let + foo = toSeq(1..10).map(x => x * 2).filter(x => x mod 6 != 0) + bar = toSeq(1..10).mapIt(it * 2).filterIt(it mod 6 != 0) + baz = collect: + for i in 1..10: + let j = 2 * i + if j mod 6 != 0: + j + + doAssert foo == bar + doAssert foo == baz + doAssert foo == @[2, 4, 8, 10, 14, 16, 20] + + doAssert foo.any(x => x > 17) + doAssert not bar.allIt(it < 20) + doAssert foo.foldl(a + b) == 74 # sum of all members + + +runnableExamples: + from std/strutils import join + + let + vowels = @"aeiou" + foo = "sequtils is an awesome module" + + doAssert (vowels is seq[char]) and (vowels == @['a', 'e', 'i', 'o', 'u']) + doAssert foo.filterIt(it notin vowels).join == "sqtls s n wsm mdl" + +## See also +## ======== ## * `strutils module<strutils.html>`_ for common string functions ## * `sugar module<sugar.html>`_ for syntactic sugar macros ## * `algorithm module<algorithm.html>`_ for common generic algorithms @@ -75,20 +82,26 @@ import std/private/since -import macros +import std/macros +from std/typetraits import supportsCopyMem -when not defined(nimhygiene): - {.pragma: dirty.} +when defined(nimPreviewSlimSystem): + import std/assertions +when defined(nimHasEffectsOf): + {.experimental: "strictEffects".} +else: + {.pragma: effectsOf.} + macro evalOnceAs(expAlias, exp: untyped, letAssigneable: static[bool]): untyped = - ## Injects ``expAlias`` in caller scope, to avoid bugs involving multiple - ## substitution in macro arguments such as - ## https://github.com/nim-lang/Nim/issues/7187 - ## ``evalOnceAs(myAlias, myExp)`` will behave as ``let myAlias = myExp`` - ## except when ``letAssigneable`` is false (e.g. to handle openArray) where - ## it just forwards ``exp`` unchanged + ## Injects `expAlias` in caller scope, to avoid bugs involving multiple + ## substitution in macro arguments such as + ## https://github.com/nim-lang/Nim/issues/7187. + ## `evalOnceAs(myAlias, myExp)` will behave as `let myAlias = myExp` + ## except when `letAssigneable` is false (e.g. to handle openArray) where + ## it just forwards `exp` unchanged. expectKind(expAlias, nnkIdent) var val = exp @@ -103,12 +116,12 @@ macro evalOnceAs(expAlias, exp: untyped, newProc(name = genSym(nskTemplate, $expAlias), params = [getType(untyped)], body = val, procType = nnkTemplateDef)) -proc concat*[T](seqs: varargs[seq[T]]): seq[T] = +func concat*[T](seqs: varargs[seq[T]]): seq[T] = ## Takes several sequences' items and returns them inside a new sequence. ## All sequences must be of the same type. ## - ## See also: - ## * `distribute proc<#distribute,seq[T],Positive>`_ for a reverse + ## **See also:** + ## * `distribute func<#distribute,seq[T],Positive>`_ for a reverse ## operation ## runnableExamples: @@ -128,7 +141,23 @@ proc concat*[T](seqs: varargs[seq[T]]): seq[T] = result[i] = itm inc(i) -proc count*[T](s: openArray[T], x: T): int = +func addUnique*[T](s: var seq[T], x: sink T) = + ## Adds `x` to the container `s` if it is not already present. + ## Uses `==` to check if the item is already present. + runnableExamples: + var a = @[1, 2, 3] + a.addUnique(4) + a.addUnique(4) + assert a == @[1, 2, 3, 4] + + for i in 0..high(s): + if s[i] == x: return + when declared(ensureMove): + s.add ensureMove(x) + else: + s.add x + +func count*[T](s: openArray[T], x: T): int = ## Returns the number of occurrences of the item `x` in the container `s`. ## runnableExamples: @@ -143,7 +172,7 @@ proc count*[T](s: openArray[T], x: T): int = if itm == x: inc result -proc cycle*[T](s: openArray[T], n: Natural): seq[T] = +func cycle*[T](s: openArray[T], n: Natural): seq[T] = ## Returns a new sequence with the items of the container `s` repeated ## `n` times. ## `n` must be a non-negative number (zero or more). @@ -174,10 +203,10 @@ proc repeat*[T](x: T, n: Natural): seq[T] = for i in 0 ..< n: result[i] = x -proc deduplicate*[T](s: openArray[T], isSorted: bool = false): seq[T] = +func deduplicate*[T](s: openArray[T], isSorted: bool = false): seq[T] = ## Returns a new sequence without duplicates. ## - ## Setting the optional argument ``isSorted`` to ``true`` (default: false) + ## Setting the optional argument `isSorted` to true (default: false) ## uses a faster algorithm for deduplication. ## runnableExamples: @@ -202,9 +231,9 @@ proc deduplicate*[T](s: openArray[T], isSorted: bool = false): seq[T] = for itm in items(s): if not result.contains(itm): result.add(itm) -proc minIndex*[T](s: openArray[T]): int {.since: (1, 1).} = +func minIndex*[T](s: openArray[T]): int {.since: (1, 1).} = ## Returns the index of the minimum value of `s`. - ## ``T`` needs to have a ``<`` operator. + ## `T` needs to have a `<` operator. runnableExamples: let a = @[1, 2, 3, 4] @@ -219,9 +248,9 @@ proc minIndex*[T](s: openArray[T]): int {.since: (1, 1).} = for i in 1..high(s): if s[i] < s[result]: result = i -proc maxIndex*[T](s: openArray[T]): int {.since: (1, 1).} = +func maxIndex*[T](s: openArray[T]): int {.since: (1, 1).} = ## Returns the index of the maximum value of `s`. - ## ``T`` needs to have a ``<`` operator. + ## `T` needs to have a `<` operator. runnableExamples: let a = @[1, 2, 3, 4] @@ -236,6 +265,15 @@ proc maxIndex*[T](s: openArray[T]): int {.since: (1, 1).} = for i in 1..high(s): if s[i] > s[result]: result = i +func minmax*[T](x: openArray[T]): (T, T) = + ## The minimum and maximum values of `x`. `T` needs to have a `<` operator. + var l = x[0] + var h = x[0] + for i in 1..high(x): + if x[i] < l: l = x[i] + if h < x[i]: h = x[i] + result = (l, h) + template zipImpl(s1, s2, retType: untyped): untyped = proc zip*[S, T](s1: openArray[S], s2: openArray[T]): retType = @@ -245,9 +283,9 @@ template zipImpl(s1, s2, retType: untyped): untyped = ## If one container is shorter, the remaining items in the longer container ## are discarded. ## - ## **Note**: For Nim 1.0.x and older version, ``zip`` returned a seq of - ## named tuple with fields ``a`` and ``b``. For Nim versions 1.1.x and newer, - ## ``zip`` returns a seq of unnamed tuples. + ## **Note**: For Nim 1.0.x and older version, `zip` returned a seq of + ## named tuples with fields `a` and `b`. For Nim versions 1.1.x and newer, + ## `zip` returns a seq of unnamed tuples. runnableExamples: let short = @[1, 2, 3] @@ -290,25 +328,24 @@ proc unzip*[S, T](s: openArray[(S, T)]): (seq[S], seq[T]) {.since: (1, 1).} = unzipped2 = @['a', 'b', 'c'] assert zipped.unzip() == (unzipped1, unzipped2) assert zip(unzipped1, unzipped2).unzip() == (unzipped1, unzipped2) - result[0] = newSeq[S](s.len) - result[1] = newSeq[T](s.len) + result = (newSeq[S](s.len), newSeq[T](s.len)) for i in 0..<s.len: result[0][i] = s[i][0] result[1][i] = s[i][1] -proc distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] = +func distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] = ## Splits and distributes a sequence `s` into `num` sub-sequences. ## ## Returns a sequence of `num` sequences. For *some* input values this is the - ## inverse of the `concat <#concat,varargs[seq[T]]>`_ proc. + ## inverse of the `concat <#concat,varargs[seq[T]]>`_ func. ## The input sequence `s` can be empty, which will produce ## `num` empty sequences. ## ## If `spread` is false and the length of `s` is not a multiple of `num`, the - ## proc will max out the first sub-sequence with ``1 + len(s) div num`` + ## func will max out the first sub-sequence with `1 + len(s) div num` ## entries, leaving the remainder of elements to the last sequence. ## - ## On the other hand, if `spread` is true, the proc will distribute evenly + ## On the other hand, if `spread` is true, the func will distribute evenly ## the remainder of the division across all sequences, which makes the result ## more suited to multithreading where you are passing equal sized work units ## to a thread pool and want to maximize core usage. @@ -323,7 +360,6 @@ proc distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] = if num < 2: result = @[s] return - let num = int(num) # XXX probably only needed because of .. bug # Create the result and calculate the stride size and the remainder if any. result = newSeq[seq[T]](num) @@ -354,14 +390,18 @@ proc distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] = first = last proc map*[T, S](s: openArray[T], op: proc (x: T): S {.closure.}): - seq[S]{.inline.} = - ## Returns a new sequence with the results of `op` proc applied to every + seq[S] {.inline, effectsOf: op.} = + ## Returns a new sequence with the results of the `op` proc applied to every ## item in the container `s`. ## - ## Since the input is not modified you can use it to + ## Since the input is not modified, you can use it to ## transform the type of the elements in the input container. ## - ## See also: + ## Instead of using `map` and `filter`, consider using the `collect` macro + ## from the `sugar` module. + ## + ## **See also:** + ## * `sugar.collect macro<sugar.html#collect.m%2Cuntyped%2Cuntyped>`_ ## * `mapIt template<#mapIt.t,typed,untyped>`_ ## * `apply proc<#apply,openArray[T],proc(T)_2>`_ for the in-place version ## @@ -376,15 +416,14 @@ proc map*[T, S](s: openArray[T], op: proc (x: T): S {.closure.}): result[i] = op(s[i]) proc apply*[T](s: var openArray[T], op: proc (x: var T) {.closure.}) - {.inline.} = - ## Applies `op` to every item in `s` modifying it directly. + {.inline, effectsOf: op.} = + ## Applies `op` to every item in `s`, modifying it directly. ## - ## Note that container `s` must be declared as a ``var`` - ## and it is required for your input and output types to - ## be the same, since `s` is modified in-place. - ## The parameter function takes a ``var T`` type parameter. + ## Note that the container `s` must be declared as a `var`, + ## since `s` is modified in-place. + ## The parameter function takes a `var T` type parameter. ## - ## See also: + ## **See also:** ## * `applyIt template<#applyIt.t,untyped,untyped>`_ ## * `map proc<#map,openArray[T],proc(T)>`_ ## @@ -396,15 +435,15 @@ proc apply*[T](s: var openArray[T], op: proc (x: var T) {.closure.}) for i in 0 ..< s.len: op(s[i]) proc apply*[T](s: var openArray[T], op: proc (x: T): T {.closure.}) - {.inline.} = + {.inline, effectsOf: op.} = ## Applies `op` to every item in `s` modifying it directly. ## - ## Note that container `s` must be declared as a ``var`` + ## Note that the container `s` must be declared as a `var` ## and it is required for your input and output types to ## be the same, since `s` is modified in-place. - ## The parameter function takes and returns a ``T`` type variable. + ## The parameter function takes and returns a `T` type variable. ## - ## See also: + ## **See also:** ## * `applyIt template<#applyIt.t,untyped,untyped>`_ ## * `map proc<#map,openArray[T],proc(T)>`_ ## @@ -415,12 +454,25 @@ proc apply*[T](s: var openArray[T], op: proc (x: T): T {.closure.}) for i in 0 ..< s.len: s[i] = op(s[i]) -iterator filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): T = +proc apply*[T](s: openArray[T], op: proc (x: T) {.closure.}) {.inline, since: (1, 3), effectsOf: op.} = + ## Same as `apply` but for a proc that does not return anything + ## and does not mutate `s` directly. + runnableExamples: + var message: string + apply([0, 1, 2, 3, 4], proc(item: int) = message.addInt item) + assert message == "01234" + for i in 0 ..< s.len: op(s[i]) + +iterator filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): T {.effectsOf: pred.} = ## Iterates through a container `s` and yields every item that fulfills the - ## predicate `pred` (function that returns a `bool`). + ## predicate `pred` (a function that returns a `bool`). + ## + ## Instead of using `map` and `filter`, consider using the `collect` macro + ## from the `sugar` module. ## - ## See also: - ## * `fliter proc<#filter,openArray[T],proc(T)>`_ + ## **See also:** + ## * `sugar.collect macro<sugar.html#collect.m%2Cuntyped%2Cuntyped>`_ + ## * `filter proc<#filter,openArray[T],proc(T)>`_ ## * `filterIt template<#filterIt.t,untyped,untyped>`_ ## runnableExamples: @@ -435,11 +487,15 @@ iterator filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): T = yield s[i] proc filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): seq[T] - {.inline.} = - ## Returns a new sequence with all the items of `s` that fulfilled the - ## predicate `pred` (function that returns a `bool`). + {.inline, effectsOf: pred.} = + ## Returns a new sequence with all the items of `s` that fulfill the + ## predicate `pred` (a function that returns a `bool`). + ## + ## Instead of using `map` and `filter`, consider using the `collect` macro + ## from the `sugar` module. ## - ## See also: + ## **See also:** + ## * `sugar.collect macro<sugar.html#collect.m%2Cuntyped%2Cuntyped>`_ ## * `filterIt template<#filterIt.t,untyped,untyped>`_ ## * `filter iterator<#filter.i,openArray[T],proc(T)>`_ ## * `keepIf proc<#keepIf,seq[T],proc(T)>`_ for the in-place version @@ -458,16 +514,16 @@ proc filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): seq[T] result.add(s[i]) proc keepIf*[T](s: var seq[T], pred: proc(x: T): bool {.closure.}) - {.inline.} = - ## Keeps the items in the passed sequence `s` if they fulfilled the - ## predicate `pred` (function that returns a `bool`). + {.inline, effectsOf: pred.} = + ## Keeps the items in the passed sequence `s` if they fulfill the + ## predicate `pred` (a function that returns a `bool`). ## - ## Note that `s` must be declared as a ``var``. + ## Note that `s` must be declared as a `var`. ## ## Similar to the `filter proc<#filter,openArray[T],proc(T)>`_, ## but modifies the sequence directly. ## - ## See also: + ## **See also:** ## * `keepItIf template<#keepItIf.t,seq,untyped>`_ ## * `filter proc<#filter,openArray[T],proc(T)>`_ ## @@ -487,12 +543,51 @@ proc keepIf*[T](s: var seq[T], pred: proc(x: T): bool {.closure.}) inc(pos) setLen(s, pos) -proc delete*[T](s: var seq[T]; first, last: Natural) = - ## Deletes in the items of a sequence `s` at positions ``first..last`` - ## (including both ends of a range). - ## This modifies `s` itself, it does not return a copy. +func delete*[T](s: var seq[T]; slice: Slice[int]) = + ## Deletes the items `s[slice]`, raising `IndexDefect` if the slice contains + ## elements out of range. ## + ## This operation moves all elements after `s[slice]` in linear time. runnableExamples: + var a = @[10, 11, 12, 13, 14] + doAssertRaises(IndexDefect): a.delete(4..5) + assert a == @[10, 11, 12, 13, 14] + a.delete(4..4) + assert a == @[10, 11, 12, 13] + a.delete(1..2) + assert a == @[10, 13] + a.delete(1..<1) # empty slice + assert a == @[10, 13] + when compileOption("boundChecks"): + if not (slice.a < s.len and slice.a >= 0 and slice.b < s.len): + raise newException(IndexDefect, $(slice: slice, len: s.len)) + if slice.b >= slice.a: + template defaultImpl = + var i = slice.a + var j = slice.b + 1 + var newLen = s.len - j + i + while i < newLen: + when defined(gcDestructors): + s[i] = move(s[j]) + else: + s[i].shallowCopy(s[j]) + inc(i) + inc(j) + setLen(s, newLen) + when nimvm: defaultImpl() + else: + when defined(js): + let n = slice.b - slice.a + 1 + let first = slice.a + {.emit: "`s`.splice(`first`, `n`);".} + else: + defaultImpl() + +func delete*[T](s: var seq[T]; first, last: Natural) {.deprecated: "use `delete(s, first..last)`".} = + ## Deletes the items of a sequence `s` at positions `first..last` + ## (including both ends of the range). + ## This modifies `s` itself, it does not return a copy. + runnableExamples("--warning:deprecated:off"): let outcome = @[1, 1, 1, 1, 1, 1, 1, 1] var dest = @[1, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1] dest.delete(3, 8) @@ -501,8 +596,8 @@ proc delete*[T](s: var seq[T]; first, last: Natural) = if first >= s.len: return var i = first - var j = min(len(s), last+1) - var newLen = len(s)-j+i + var j = min(len(s), last + 1) + var newLen = len(s) - j + i while i < newLen: when defined(gcDestructors): s[i] = move(s[j]) @@ -512,11 +607,11 @@ proc delete*[T](s: var seq[T]; first, last: Natural) = inc(j) setLen(s, newLen) -proc insert*[T](dest: var seq[T], src: openArray[T], pos = 0) = +func insert*[T](dest: var seq[T], src: openArray[T], pos = 0) = ## Inserts items from `src` into `dest` at position `pos`. This modifies ## `dest` itself, it does not return a copy. ## - ## Notice that `src` and `dest` must be of the same type. + ## Note that the elements of `src` and `dest` must be of the same type. ## runnableExamples: var dest = @[1, 1, 1, 1, 1, 1, 1, 1] @@ -527,7 +622,8 @@ proc insert*[T](dest: var seq[T], src: openArray[T], pos = 0) = assert dest == outcome var j = len(dest) - 1 - var i = len(dest) + len(src) - 1 + var i = j + len(src) + if i == j: return dest.setLen(i + 1) # Move items after `pos` to the end of the sequence. @@ -546,16 +642,20 @@ proc insert*[T](dest: var seq[T], src: openArray[T], pos = 0) = template filterIt*(s, pred: untyped): untyped = - ## Returns a new sequence with all the items of `s` that fulfilled the + ## Returns a new sequence with all the items of `s` that fulfill the ## predicate `pred`. ## ## Unlike the `filter proc<#filter,openArray[T],proc(T)>`_ and ## `filter iterator<#filter.i,openArray[T],proc(T)>`_, - ## the predicate needs to be an expression using the ``it`` variable - ## for testing, like: ``filterIt("abcxyz", it == 'x')``. + ## the predicate needs to be an expression using the `it` variable + ## for testing, like: `filterIt("abcxyz", it == 'x')`. ## - ## See also: - ## * `fliter proc<#filter,openArray[T],proc(T)>`_ + ## Instead of using `mapIt` and `filterIt`, consider using the `collect` macro + ## from the `sugar` module. + ## + ## **See also:** + ## * `sugar.collect macro<sugar.html#collect.m%2Cuntyped%2Cuntyped>`_ + ## * `filter proc<#filter,openArray[T],proc(T)>`_ ## * `filter iterator<#filter.i,openArray[T],proc(T)>`_ ## runnableExamples: @@ -572,14 +672,14 @@ template filterIt*(s, pred: untyped): untyped = result template keepItIf*(varSeq: seq, pred: untyped) = - ## Keeps the items in the passed sequence (must be declared as a ``var``) - ## if they fulfilled the predicate. + ## Keeps the items in the passed sequence (must be declared as a `var`) + ## if they fulfill the predicate. ## ## Unlike the `keepIf proc<#keepIf,seq[T],proc(T)>`_, ## the predicate needs to be an expression using - ## the ``it`` variable for testing, like: ``keepItIf("abcxyz", it == 'x')``. + ## the `it` variable for testing, like: `keepItIf("abcxyz", it == 'x')`. ## - ## See also: + ## **See also:** ## * `keepIf proc<#keepIf,seq[T],proc(T)>`_ ## * `filterIt template<#filterIt.t,untyped,untyped>`_ ## @@ -602,10 +702,10 @@ template keepItIf*(varSeq: seq, pred: untyped) = since (1, 1): template countIt*(s, pred: untyped): int = - ## Returns a count of all the items that fulfilled the predicate. + ## Returns a count of all the items that fulfill the predicate. ## ## The predicate needs to be an expression using - ## the ``it`` variable for testing, like: ``countIt(@[1, 2, 3], it > 2)``. + ## the `it` variable for testing, like: `countIt(@[1, 2, 3], it > 2)`. ## runnableExamples: let numbers = @[-3, -2, -1, 0, 1, 2, 3, 4, 5, 6] @@ -619,23 +719,23 @@ since (1, 1): if pred: result += 1 result -proc all*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): bool = +proc all*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): bool {.effectsOf: pred.} = ## Iterates through a container and checks if every item fulfills the ## predicate. ## - ## See also: + ## **See also:** ## * `allIt template<#allIt.t,untyped,untyped>`_ ## * `any proc<#any,openArray[T],proc(T)>`_ ## runnableExamples: let numbers = @[1, 4, 5, 8, 9, 7, 4] - assert all(numbers, proc (x: int): bool = return x < 10) == true - assert all(numbers, proc (x: int): bool = return x < 9) == false + assert all(numbers, proc (x: int): bool = x < 10) == true + assert all(numbers, proc (x: int): bool = x < 9) == false for i in s: if not pred(i): return false - return true + true template allIt*(s, pred: untyped): bool = ## Iterates through a container and checks if every item fulfills the @@ -643,9 +743,9 @@ template allIt*(s, pred: untyped): bool = ## ## Unlike the `all proc<#all,openArray[T],proc(T)>`_, ## the predicate needs to be an expression using - ## the ``it`` variable for testing, like: ``allIt("abba", it == 'a')``. + ## the `it` variable for testing, like: `allIt("abba", it == 'a')`. ## - ## See also: + ## **See also:** ## * `all proc<#all,openArray[T],proc(T)>`_ ## * `anyIt template<#anyIt.t,untyped,untyped>`_ ## @@ -661,33 +761,33 @@ template allIt*(s, pred: untyped): bool = break result -proc any*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): bool = - ## Iterates through a container and checks if some item fulfills the - ## predicate. +proc any*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): bool {.effectsOf: pred.} = + ## Iterates through a container and checks if at least one item + ## fulfills the predicate. ## - ## See also: + ## **See also:** ## * `anyIt template<#anyIt.t,untyped,untyped>`_ ## * `all proc<#all,openArray[T],proc(T)>`_ ## runnableExamples: let numbers = @[1, 4, 5, 8, 9, 7, 4] - assert any(numbers, proc (x: int): bool = return x > 8) == true - assert any(numbers, proc (x: int): bool = return x > 9) == false + assert any(numbers, proc (x: int): bool = x > 8) == true + assert any(numbers, proc (x: int): bool = x > 9) == false for i in s: if pred(i): return true - return false + false template anyIt*(s, pred: untyped): bool = - ## Iterates through a container and checks if some item fulfills the - ## predicate. + ## Iterates through a container and checks if at least one item + ## fulfills the predicate. ## ## Unlike the `any proc<#any,openArray[T],proc(T)>`_, ## the predicate needs to be an expression using - ## the ``it`` variable for testing, like: ``anyIt("abba", it == 'a')``. + ## the `it` variable for testing, like: `anyIt("abba", it == 'a')`. ## - ## See also: + ## **See also:** ## * `any proc<#any,openArray[T],proc(T)>`_ ## * `allIt template<#allIt.t,untyped,untyped>`_ ## @@ -716,7 +816,7 @@ template toSeq1(s: not iterator): untyped = i += 1 result else: - var result: seq[OutType] = @[] + var result: seq[OutType]# = @[] for it in s: result.add(it) result @@ -733,7 +833,7 @@ template toSeq2(iter: iterator): untyped = result else: type OutType = typeof(iter2()) - var result: seq[OutType] = @[] + var result: seq[OutType]# = @[] when compiles(iter2()): evalOnceAs(iter4, iter, false) let iter3 = iter4() @@ -788,15 +888,15 @@ template foldl*(sequence, operation: untyped): untyped = ## The sequence is required to have at least a single element. Debug versions ## of your program will assert in this situation but release versions will ## happily go ahead. If the sequence has a single element it will be returned - ## without applying ``operation``. + ## without applying `operation`. ## - ## The ``operation`` parameter should be an expression which uses the - ## variables ``a`` and ``b`` for each step of the fold. Since this is a left + ## The `operation` parameter should be an expression which uses the + ## variables `a` and `b` for each step of the fold. Since this is a left ## fold, for non associative binary operations like subtraction think that ## the sequence of numbers 1, 2 and 3 will be parenthesized as (((1) - 2) - ## 3). ## - ## See also: + ## **See also:** ## * `foldl template<#foldl.t,,,>`_ with a starting parameter ## * `foldr template<#foldr.t,untyped,untyped>`_ ## @@ -808,10 +908,17 @@ template foldl*(sequence, operation: untyped): untyped = multiplication = foldl(numbers, a * b) words = @["nim", "is", "cool"] concatenation = foldl(words, a & b) + procs = @["proc", "Is", "Also", "Fine"] + + + func foo(acc, cur: string): string = + result = acc & cur + assert addition == 25, "Addition is (((5)+9)+11)" assert subtraction == -15, "Subtraction is (((5)-9)-11)" assert multiplication == 495, "Multiplication is (((5)*9)*11)" assert concatenation == "nimiscool" + assert foldl(procs, foo(a, b)) == "procIsAlsoFine" let s = sequence assert s.len > 0, "Can't fold empty sequences" @@ -827,14 +934,14 @@ template foldl*(sequence, operation: untyped): untyped = template foldl*(sequence, operation, first): untyped = ## Template to fold a sequence from left to right, returning the accumulation. ## - ## This version of ``foldl`` gets a **starting parameter**. This makes it possible + ## This version of `foldl` gets a **starting parameter**. This makes it possible ## to accumulate the sequence into a different type than the sequence elements. ## - ## The ``operation`` parameter should be an expression which uses the variables - ## ``a`` and ``b`` for each step of the fold. The ``first`` parameter is the - ## start value (the first ``a``) and therefor defines the type of the result. + ## The `operation` parameter should be an expression which uses the variables + ## `a` and `b` for each step of the fold. The `first` parameter is the + ## start value (the first `a`) and therefore defines the type of the result. ## - ## See also: + ## **See also:** ## * `foldr template<#foldr.t,untyped,untyped>`_ ## runnableExamples: @@ -857,15 +964,15 @@ template foldr*(sequence, operation: untyped): untyped = ## The sequence is required to have at least a single element. Debug versions ## of your program will assert in this situation but release versions will ## happily go ahead. If the sequence has a single element it will be returned - ## without applying ``operation``. + ## without applying `operation`. ## - ## The ``operation`` parameter should be an expression which uses the - ## variables ``a`` and ``b`` for each step of the fold. Since this is a right + ## The `operation` parameter should be an expression which uses the + ## variables `a` and `b` for each step of the fold. Since this is a right ## fold, for non associative binary operations like subtraction think that ## the sequence of numbers 1, 2 and 3 will be parenthesized as (1 - (2 - ## (3))). ## - ## See also: + ## **See also:** ## * `foldl template<#foldl.t,untyped,untyped>`_ ## * `foldl template<#foldl.t,,,>`_ with a starting parameter ## @@ -894,16 +1001,20 @@ template foldr*(sequence, operation: untyped): untyped = result template mapIt*(s: typed, op: untyped): untyped = - ## Returns a new sequence with the results of `op` proc applied to every + ## Returns a new sequence with the results of the `op` proc applied to every ## item in the container `s`. ## ## Since the input is not modified you can use it to ## transform the type of the elements in the input container. ## - ## The template injects the ``it`` variable which you can use directly in an + ## The template injects the `it` variable which you can use directly in an ## expression. ## - ## See also: + ## Instead of using `mapIt` and `filterIt`, consider using the `collect` macro + ## from the `sugar` module. + ## + ## **See also:** + ## * `sugar.collect macro<sugar.html#collect.m%2Cuntyped%2Cuntyped>`_ ## * `map proc<#map,openArray[T],proc(T)>`_ ## * `applyIt template<#applyIt.t,untyped,untyped>`_ for the in-place version ## @@ -913,16 +1024,10 @@ template mapIt*(s: typed, op: untyped): untyped = strings = nums.mapIt($(4 * it)) assert strings == @["4", "8", "12", "16"] - when defined(nimHasTypeof): - type OutType = typeof(( - block: - var it{.inject.}: typeof(items(s), typeOfIter); - op), typeOfProc) - else: - type OutType = typeof(( - block: - var it{.inject.}: typeof(items(s)); - op)) + type OutType = typeof(( + block: + var it{.inject.}: typeof(items(s), typeOfIter); + op), typeOfProc) when OutType is not (proc): # Here, we avoid to create closures in loops. # This avoids https://github.com/nim-lang/Nim/issues/12625 @@ -940,7 +1045,7 @@ template mapIt*(s: typed, op: untyped): untyped = i += 1 result else: - var result: seq[OutType] = @[] + var result: seq[OutType]# = @[] # use `items` to avoid https://github.com/nim-lang/Nim/issues/12639 for it {.inject.} in items(s): result.add(op) @@ -953,11 +1058,7 @@ template mapIt*(s: typed, op: untyped): untyped = # With this fallback, above code can be simplified to: # [1, 2].mapIt((x: int) => it + x) # In this case, `mapIt` is just syntax sugar for `map`. - - when defined(nimHasTypeof): - type InType = typeof(items(s), typeOfIter) - else: - type InType = typeof(items(s)) + type InType = typeof(items(s), typeOfIter) # Use a help proc `f` to create closures for each element in `s` let f = proc (x: InType): OutType = let it {.inject.} = x @@ -965,13 +1066,13 @@ template mapIt*(s: typed, op: untyped): untyped = map(s, f) template applyIt*(varSeq, op: untyped) = - ## Convenience template around the mutable ``apply`` proc to reduce typing. + ## Convenience template around the mutable `apply` proc to reduce typing. ## - ## The template injects the ``it`` variable which you can use directly in an - ## expression. The expression has to return the same type as the sequence you - ## are mutating. + ## The template injects the `it` variable which you can use directly in an + ## expression. The expression has to return the same type as the elements + ## of the sequence you are mutating. ## - ## See also: + ## **See also:** ## * `apply proc<#apply,openArray[T],proc(T)_2>`_ ## * `mapIt template<#mapIt.t,typed,untyped>`_ ## @@ -986,29 +1087,33 @@ template applyIt*(varSeq, op: untyped) = template newSeqWith*(len: int, init: untyped): untyped = - ## Creates a new sequence of length `len`, calling `init` to initialize - ## each value of the sequence. - ## - ## Useful for creating "2D" sequences - sequences containing other sequences - ## or to populate fields of the created sequence. + ## Creates a new `seq` of length `len`, calling `init` to initialize + ## each value of the seq. ## + ## Useful for creating "2D" seqs - seqs containing other seqs + ## or to populate fields of the created seq. runnableExamples: - ## Creates a sequence containing 5 bool sequences, each of length of 3. + ## Creates a seq containing 5 bool seqs, each of length of 3. var seq2D = newSeqWith(5, newSeq[bool](3)) assert seq2D.len == 5 assert seq2D[0].len == 3 assert seq2D[4][2] == false - ## Creates a sequence of 20 random numbers from 1 to 10 - import random - var seqRand = newSeqWith(20, rand(10)) - - var result = newSeq[typeof(init)](len) - for i in 0 ..< len: + ## Creates a seq with random numbers + import std/random + var seqRand = newSeqWith(20, rand(1.0)) + assert seqRand[0] != seqRand[1] + type T = typeof(init) + let newLen = len + when supportsCopyMem(T) and declared(newSeqUninit): + var result = newSeqUninit[T](newLen) + else: # TODO: use `newSeqUnsafe` when that's available + var result = newSeq[T](newLen) + for i in 0 ..< newLen: result[i] = init - result + move(result) # refs bug #7295 -proc mapLitsImpl(constructor: NimNode; op: NimNode; nested: bool; +func mapLitsImpl(constructor: NimNode; op: NimNode; nested: bool; filter = nnkLiterals): NimNode = if constructor.kind in filter: result = newNimNode(nnkCall, lineInfoFrom = constructor) @@ -1024,44 +1129,34 @@ proc mapLitsImpl(constructor: NimNode; op: NimNode; nested: bool; macro mapLiterals*(constructor, op: untyped; nested = true): untyped = - ## Applies ``op`` to each of the **atomic** literals like ``3`` - ## or ``"abc"`` in the specified ``constructor`` AST. This can + ## Applies `op` to each of the **atomic** literals like `3` + ## or `"abc"` in the specified `constructor` AST. This can ## be used to map every array element to some target type: - ## - ## Example: - ## - ## .. code-block:: - ## let x = mapLiterals([0.1, 1.2, 2.3, 3.4], int) - ## doAssert x is array[4, int] - ## - ## Short notation for: - ## - ## .. code-block:: - ## let x = [int(0.1), int(1.2), int(2.3), int(3.4)] - ## - ## If ``nested`` is true (which is the default), the literals are replaced - ## everywhere in the ``constructor`` AST, otherwise only the first level + runnableExamples: + let x = mapLiterals([0.1, 1.2, 2.3, 3.4], int) + doAssert x is array[4, int] + doAssert x == [int(0.1), int(1.2), int(2.3), int(3.4)] + ## If `nested` is true (which is the default), the literals are replaced + ## everywhere in the `constructor` AST, otherwise only the first level ## is considered: - ## - ## .. code-block:: - ## let a = mapLiterals((1.2, (2.3, 3.4), 4.8), int) - ## let b = mapLiterals((1.2, (2.3, 3.4), 4.8), int, nested=false) - ## assert a == (1, (2, 3), 4) - ## assert b == (1, (2.3, 3.4), 4) - ## - ## let c = mapLiterals((1, (2, 3), 4, (5, 6)), `$`) - ## let d = mapLiterals((1, (2, 3), 4, (5, 6)), `$`, nested=false) - ## assert c == ("1", ("2", "3"), "4", ("5", "6")) - ## assert d == ("1", (2, 3), "4", (5, 6)) - ## - ## There are no constraints for the ``constructor`` AST, it + runnableExamples: + let a = mapLiterals((1.2, (2.3, 3.4), 4.8), int) + let b = mapLiterals((1.2, (2.3, 3.4), 4.8), int, nested=false) + assert a == (1, (2, 3), 4) + assert b == (1, (2.3, 3.4), 4) + + let c = mapLiterals((1, (2, 3), 4, (5, 6)), `$`) + let d = mapLiterals((1, (2, 3), 4, (5, 6)), `$`, nested=false) + assert c == ("1", ("2", "3"), "4", ("5", "6")) + assert d == ("1", (2, 3), "4", (5, 6)) + ## There are no constraints for the `constructor` AST, it ## works for nested tuples of arrays of sets etc. result = mapLitsImpl(constructor, op, nested.boolVal) iterator items*[T](xs: iterator: T): T = - ## iterates over each element yielded by a closure iterator. This may + ## Iterates over each element yielded by a closure iterator. This may ## not seem particularly useful on its own, but this allows closure - ## iterators to be used by the the mapIt, filterIt, allIt, anyIt, etc. + ## iterators to be used by the mapIt, filterIt, allIt, anyIt, etc. ## templates. for x in xs(): yield x diff --git a/lib/pure/collections/setimpl.nim b/lib/pure/collections/setimpl.nim index d798cbcb3..360a075d6 100644 --- a/lib/pure/collections/setimpl.nim +++ b/lib/pure/collections/setimpl.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -# An ``include`` file for the different hash set implementations. +# An `include` file for the different hash set implementations. template maxHash(t): untyped = high(t.data) @@ -16,12 +16,12 @@ template dataLen(t): untyped = len(t.data) include hashcommon template initImpl(s: typed, size: int) = - assert isPowerOfTwo(size) + let correctSize = slotsNeeded(size) when s is OrderedSet: s.first = -1 s.last = -1 s.counter = 0 - newSeq(s.data, size) + newSeq(s.data, correctSize) template rawInsertImpl() {.dirty.} = if data.len == 0: @@ -62,6 +62,7 @@ template containsOrInclImpl() {.dirty.} = if index >= 0: result = true else: + result = false if mustRehash(s): enlarge(s) index = rawGetKnownHC(s, key, hc) @@ -86,7 +87,9 @@ proc exclImpl[A](s: var HashSet[A], key: A): bool {.inline.} = var j = i # The correctness of this depends on (h+1) in nextTry, var r = j # though may be adaptable to other simple sequences. s.data[i].hcode = 0 # mark current EMPTY - s.data[i].key = default(type(s.data[i].key)) + {.push warning[UnsafeDefault]:off.} + reset(s.data[i].key) + {.pop.} doWhile((i >= r and r > j) or (r > j and j > i) or (j > i and i >= r)): i = (i + 1) and msk # increment mod table size if isEmpty(s.data[i].hcode): # end of collision cluster; So all done diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim index 2b270c2cb..af13135aa 100644 --- a/lib/pure/collections/sets.nim +++ b/lib/pure/collections/sets.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -## The ``sets`` module implements an efficient `hash set`:idx: and +## The `sets` module implements an efficient `hash set`:idx: and ## ordered hash set. ## ## Hash sets are different from the `built in set type @@ -16,7 +16,7 @@ ## ## Common usages of sets: ## * removing duplicates from a container by converting it with `toHashSet proc -## <#toHashSet,openArray[A]>`_ (see also `sequtils.deduplicate proc +## <#toHashSet,openArray[A]>`_ (see also `sequtils.deduplicate func ## <sequtils.html#deduplicate,openArray[T],bool>`_) ## * membership testing ## * mathematical operations on two sets, such as @@ -25,7 +25,9 @@ ## `difference <#difference,HashSet[A],HashSet[A]>`_, and ## `symmetric difference <#symmetricDifference,HashSet[A],HashSet[A]>`_ ## -## .. code-block:: +## **Examples:** +## +## ```Nim ## echo toHashSet([9, 5, 1]) # {9, 1, 5} ## echo toOrderedSet([9, 5, 1]) # {9, 5, 1} ## @@ -37,10 +39,10 @@ ## echo s1 - s2 # {1, 9} ## echo s1 * s2 # {5} ## echo s1 -+- s2 # {9, 1, 3, 7} -## +## ``` ## ## Note: The data types declared here have *value semantics*: This means -## that ``=`` performs a copy of the set. +## that `=` performs a copy of the set. ## ## **See also:** ## * `intsets module <intsets.html>`_ for efficient int sets @@ -48,12 +50,12 @@ import - hashes, math + std/[hashes, math] -{.pragma: myShallow.} -when not defined(nimhygiene): - {.pragma: dirty.} +when not defined(nimHasEffectsOf): + {.pragma: effectsOf.} +{.pragma: myShallow.} # For "integer-like A" that are too big for intsets/bit-vectors to be practical, # it would be best to shrink hcode to the same size as the integer. Larger # codes should never be needed, and this can pack more entries per cache-line. @@ -64,7 +66,7 @@ type HashSet*[A] {.myShallow.} = object ## \ ## A generic hash set. ## - ## Use `init proc <#init,HashSet[A],int>`_ or `initHashSet proc <#initHashSet,int>`_ + ## Use `init proc <#init,HashSet[A]>`_ or `initHashSet proc <#initHashSet>`_ ## before calling other procs on it. data: KeyValuePairSeq[A] counter: int @@ -76,10 +78,12 @@ type OrderedSet*[A] {.myShallow.} = object ## \ ## A generic hash set that remembers insertion order. ## - ## Use `init proc <#init,OrderedSet[A],int>`_ or `initOrderedSet proc - ## <#initOrderedSet,int>`_ before calling other procs on it. + ## Use `init proc <#init,OrderedSet[A]>`_ or `initOrderedSet proc + ## <#initOrderedSet>`_ before calling other procs on it. data: OrderedKeyValuePairSeq[A] counter, first, last: int + SomeSet*[A] = HashSet[A] | OrderedSet[A] + ## Type union representing `HashSet` or `OrderedSet`. const defaultInitialSize* = 64 @@ -94,11 +98,6 @@ include setimpl proc init*[A](s: var HashSet[A], initialSize = defaultInitialSize) = ## Initializes a hash set. ## - ## The `initialSize` parameter needs to be a power of two (default: 64). - ## If you need to accept runtime values for this, you can use - ## `math.nextPowerOfTwo proc <math.html#nextPowerOfTwo,int>`_ or - ## `rightSize proc <#rightSize,Natural>`_ from this module. - ## ## Starting from Nim v0.20, sets are initialized by default and it is ## not necessary to call this function explicitly. ## @@ -107,7 +106,7 @@ proc init*[A](s: var HashSet[A], initialSize = defaultInitialSize) = ## existing values and calling `excl() <#excl,HashSet[A],A>`_ on them. ## ## See also: - ## * `initHashSet proc <#initHashSet,int>`_ + ## * `initHashSet proc <#initHashSet>`_ ## * `toHashSet proc <#toHashSet,openArray[A]>`_ runnableExamples: var a: HashSet[int] @@ -116,10 +115,10 @@ proc init*[A](s: var HashSet[A], initialSize = defaultInitialSize) = initImpl(s, initialSize) proc initHashSet*[A](initialSize = defaultInitialSize): HashSet[A] = - ## Wrapper around `init proc <#init,HashSet[A],int>`_ for initialization of + ## Wrapper around `init proc <#init,HashSet[A]>`_ for initialization of ## hash sets. ## - ## Returns an empty hash set you can assign directly in ``var`` blocks in a + ## Returns an empty hash set you can assign directly in `var` blocks in a ## single line. ## ## Starting from Nim v0.20, sets are initialized by default and it is @@ -131,12 +130,12 @@ proc initHashSet*[A](initialSize = defaultInitialSize): HashSet[A] = var a = initHashSet[int]() a.incl(3) assert len(a) == 1 - + result = default(HashSet[A]) result.init(initialSize) proc `[]`*[A](s: var HashSet[A], key: A): var A = ## Returns the element that is actually stored in `s` which has the same - ## value as `key` or raises the ``KeyError`` exception. + ## value as `key` or raises the `KeyError` exception. ## ## This is useful when one overloaded `hash` and `==` but still needs ## reference semantics for sharing. @@ -170,6 +169,27 @@ proc contains*[A](s: HashSet[A], key: A): bool = var index = rawGet(s, key, hc) result = index >= 0 +proc len*[A](s: HashSet[A]): int = + ## Returns the number of elements in `s`. + ## + ## Due to an implementation detail you can call this proc on variables which + ## have not been initialized yet. The proc will return zero as the length + ## then. + runnableExamples: + var a: HashSet[string] + assert len(a) == 0 + let s = toHashSet([3, 5, 7]) + assert len(s) == 3 + + result = s.counter + +proc card*[A](s: HashSet[A]): int = + ## Alias for `len() <#len,HashSet[A]>`_. + ## + ## Card stands for the `cardinality + ## <http://en.wikipedia.org/wiki/Cardinality>`_ of a set. + result = s.counter + proc incl*[A](s: var HashSet[A], key: A) = ## Includes an element `key` in `s`. ## @@ -212,7 +232,7 @@ proc toHashSet*[A](keys: openArray[A]): HashSet[A] = ## Duplicates are removed. ## ## See also: - ## * `initHashSet proc <#initHashSet,int>`_ + ## * `initHashSet proc <#initHashSet>`_ runnableExamples: let a = toHashSet([5, 3, 2]) @@ -222,7 +242,7 @@ proc toHashSet*[A](keys: openArray[A]): HashSet[A] = assert len(b) == 5 ## b == {'a', 'b', 'c', 'd', 'r'} - result = initHashSet[A](rightSize(keys.len)) + result = initHashSet[A](keys.len) for key in items(keys): result.incl(key) iterator items*[A](s: HashSet[A]): A = @@ -231,7 +251,7 @@ iterator items*[A](s: HashSet[A]): A = ## If you need a sequence with the elements you can use `sequtils.toSeq ## template <sequtils.html#toSeq.t,untyped>`_. ## - ## .. code-block:: + ## ```Nim ## type ## pair = tuple[a, b: int] ## var @@ -244,8 +264,12 @@ iterator items*[A](s: HashSet[A]): A = ## assert a.len == 2 ## echo b ## # --> {(a: 1, b: 3), (a: 0, b: 4)} + ## ``` + let length = s.len for h in 0 .. high(s.data): - if isFilled(s.data[h].hcode): yield s.data[h].key + if isFilled(s.data[h].hcode): + yield s.data[h].key + assert(len(s) == length, "the length of the HashSet changed while iterating over it") proc containsOrIncl*[A](s: var HashSet[A], key: A): bool = ## Includes `key` in the set `s` and tells if `key` was already in `s`. @@ -324,9 +348,9 @@ proc missingOrExcl*[A](s: var HashSet[A], key: A): bool = exclImpl(s, key) proc pop*[A](s: var HashSet[A]): A = - ## Remove and return an arbitrary element from the set `s`. + ## Removes and returns an arbitrary element from the set `s`. ## - ## Raises KeyError if the set `s` is empty. + ## Raises `KeyError` if the set `s` is empty. ## ## See also: ## * `clear proc <#clear,HashSet[A]>`_ @@ -358,28 +382,9 @@ proc clear*[A](s: var HashSet[A]) = s.counter = 0 for i in 0 ..< s.data.len: s.data[i].hcode = 0 - s.data[i].key = default(type(s.data[i].key)) - -proc len*[A](s: HashSet[A]): int = - ## Returns the number of elements in `s`. - ## - ## Due to an implementation detail you can call this proc on variables which - ## have not been initialized yet. The proc will return zero as the length - ## then. - runnableExamples: - var a: HashSet[string] - assert len(a) == 0 - let s = toHashSet([3, 5, 7]) - assert len(s) == 3 - - result = s.counter - -proc card*[A](s: HashSet[A]): int = - ## Alias for `len() <#len,HashSet[A]>`_. - ## - ## Card stands for the `cardinality - ## <http://en.wikipedia.org/wiki/Cardinality>`_ of a set. - result = s.counter + {.push warning[UnsafeDefault]:off.} + reset(s.data[i].key) + {.pop.} proc union*[A](s1, s2: HashSet[A]): HashSet[A] = @@ -425,8 +430,15 @@ proc intersection*[A](s1, s2: HashSet[A]): HashSet[A] = assert c == toHashSet(["b"]) result = initHashSet[A](max(min(s1.data.len, s2.data.len), 2)) - for item in s1: - if item in s2: incl(result, item) + + # iterate over the elements of the smaller set + if s1.data.len < s2.data.len: + for item in s1: + if item in s2: incl(result, item) + else: + for item in s2: + if item in s1: incl(result, item) + proc difference*[A](s1, s2: HashSet[A]): HashSet[A] = ## Returns the difference of the sets `s1` and `s2`. @@ -552,7 +564,7 @@ proc `==`*[A](s, t: HashSet[A]): bool = s.counter == t.counter and s <= t -proc map*[A, B](data: HashSet[A], op: proc (x: A): B {.closure.}): HashSet[B] = +proc map*[A, B](data: HashSet[A], op: proc (x: A): B {.closure.}): HashSet[B] {.effectsOf: op.} = ## Returns a new set after applying `op` proc on each of the elements of ##`data` set. ## @@ -579,12 +591,12 @@ proc `$`*[A](s: HashSet[A]): string = ## any moment and values are not escaped. ## ## **Examples:** - ## - ## .. code-block:: + ## ```Nim ## echo toHashSet([2, 4, 5]) ## # --> {2, 4, 5} ## echo toHashSet(["no", "esc'aping", "is \" provided"]) ## # --> {no, esc'aping, is " provided} + ## ``` dollarImpl() @@ -597,14 +609,12 @@ proc toSet*[A](keys: openArray[A]): HashSet[A] {.deprecated: proc isValid*[A](s: HashSet[A]): bool {.deprecated: "Deprecated since v0.20; sets are initialized by default".} = ## Returns `true` if the set has been initialized (with `initHashSet proc - ## <#initHashSet,int>`_ or `init proc <#init,HashSet[A],int>`_). + ## <#initHashSet>`_ or `init proc <#init,HashSet[A]>`_). ## - ## **Examples:** - ## - ## .. code-block :: - ## proc savePreferences(options: HashSet[string]) = - ## assert options.isValid, "Pass an initialized set!" - ## # Do stuff here, may crash in release builds! + runnableExamples: + proc savePreferences(options: HashSet[string]) = + assert options.isValid, "Pass an initialized set!" + # Do stuff here, may crash in release builds! result = s.data.len > 0 @@ -628,11 +638,6 @@ template forAllOrderedPairs(yieldStmt: untyped) {.dirty.} = proc init*[A](s: var OrderedSet[A], initialSize = defaultInitialSize) = ## Initializes an ordered hash set. ## - ## The `initialSize` parameter needs to be a power of two (default: 64). - ## If you need to accept runtime values for this, you can use - ## `math.nextPowerOfTwo proc <math.html#nextPowerOfTwo,int>`_ or - ## `rightSize proc <#rightSize,Natural>`_ from this module. - ## ## Starting from Nim v0.20, sets are initialized by default and it is ## not necessary to call this function explicitly. ## @@ -641,7 +646,7 @@ proc init*[A](s: var OrderedSet[A], initialSize = defaultInitialSize) = ## existing values and calling `excl() <#excl,HashSet[A],A>`_ on them. ## ## See also: - ## * `initOrderedSet proc <#initOrderedSet,int>`_ + ## * `initOrderedSet proc <#initOrderedSet>`_ ## * `toOrderedSet proc <#toOrderedSet,openArray[A]>`_ runnableExamples: var a: OrderedSet[int] @@ -650,10 +655,10 @@ proc init*[A](s: var OrderedSet[A], initialSize = defaultInitialSize) = initImpl(s, initialSize) proc initOrderedSet*[A](initialSize = defaultInitialSize): OrderedSet[A] = - ## Wrapper around `init proc <#init,OrderedSet[A],int>`_ for initialization of + ## Wrapper around `init proc <#init,OrderedSet[A]>`_ for initialization of ## ordered hash sets. ## - ## Returns an empty ordered hash set you can assign directly in ``var`` blocks + ## Returns an empty ordered hash set you can assign directly in `var` blocks ## in a single line. ## ## Starting from Nim v0.20, sets are initialized by default and it is @@ -675,7 +680,7 @@ proc toOrderedSet*[A](keys: openArray[A]): OrderedSet[A] = ## Duplicates are removed. ## ## See also: - ## * `initOrderedSet proc <#initOrderedSet,int>`_ + ## * `initOrderedSet proc <#initOrderedSet>`_ runnableExamples: let a = toOrderedSet([5, 3, 2]) @@ -685,7 +690,7 @@ proc toOrderedSet*[A](keys: openArray[A]): OrderedSet[A] = assert len(b) == 5 ## b == {'a', 'b', 'r', 'c', 'd'} # different than in HashSet - result = initOrderedSet[A](rightSize(keys.len)) + result = initOrderedSet[A](keys.len) for key in items(keys): result.incl(key) proc contains*[A](s: OrderedSet[A], key: A): bool = @@ -813,7 +818,9 @@ proc clear*[A](s: var OrderedSet[A]) = for i in 0 ..< s.data.len: s.data[i].hcode = 0 s.data[i].next = 0 - s.data[i].key = default(type(s.data[i].key)) + {.push warning[UnsafeDefault]:off.} + reset(s.data[i].key) + {.pop.} proc len*[A](s: OrderedSet[A]): int {.inline.} = ## Returns the number of elements in `s`. @@ -874,12 +881,12 @@ proc `$`*[A](s: OrderedSet[A]): string = ## any moment and values are not escaped. ## ## **Examples:** - ## - ## .. code-block:: + ## ```Nim ## echo toOrderedSet([2, 4, 5]) ## # --> {2, 4, 5} ## echo toOrderedSet(["no", "esc'aping", "is \" provided"]) ## # --> {no, esc'aping, is " provided} + ## ``` dollarImpl() @@ -890,7 +897,7 @@ iterator items*[A](s: OrderedSet[A]): A = ## If you need a sequence with the elements you can use `sequtils.toSeq ## template <sequtils.html#toSeq.t,untyped>`_. ## - ## .. code-block:: + ## ```Nim ## var a = initOrderedSet[int]() ## for value in [9, 2, 1, 5, 1, 8, 4, 2]: ## a.incl(value) @@ -902,8 +909,11 @@ iterator items*[A](s: OrderedSet[A]): A = ## # --> Got 5 ## # --> Got 8 ## # --> Got 4 + ## ``` + let length = s.len forAllOrderedPairs: yield s.data[h].key + assert(len(s) == length, "the length of the OrderedSet changed while iterating over it") iterator pairs*[A](s: OrderedSet[A]): tuple[a: int, b: A] = ## Iterates through (position, value) tuples of OrderedSet `s`. @@ -914,289 +924,7 @@ iterator pairs*[A](s: OrderedSet[A]): tuple[a: int, b: A] = p.add(x) assert p == @[(0, 'a'), (1, 'b'), (2, 'r'), (3, 'c'), (4, 'd')] + let length = s.len forAllOrderedPairs: yield (idx, s.data[h].key) - - - -# ----------------------------------------------------------------------- - - - -when isMainModule and not defined(release): - proc testModule() = - ## Internal micro test to validate docstrings and such. - block lenTest: - var values: HashSet[int] - assert values.len == 0 - assert values.card == 0 - - block setIterator: - type pair = tuple[a, b: int] - var a, b = initHashSet[pair]() - a.incl((2, 3)) - a.incl((3, 2)) - a.incl((2, 3)) - for x, y in a.items: - b.incl((x - 2, y + 1)) - assert a.len == b.card - assert a.len == 2 - #echo b - - block setContains: - var values = initHashSet[int]() - assert(not values.contains(2)) - values.incl(2) - assert values.contains(2) - values.excl(2) - assert(not values.contains(2)) - - values.incl(4) - var others = toHashSet([6, 7]) - values.incl(others) - assert values.len == 3 - - values.init - assert values.containsOrIncl(2) == false - assert values.containsOrIncl(2) == true - var - a = toHashSet([1, 2]) - b = toHashSet([1]) - b.incl(2) - assert a == b - - block exclusions: - var s = toHashSet([2, 3, 6, 7]) - s.excl(2) - s.excl(2) - assert s.len == 3 - - var - numbers = toHashSet([1, 2, 3, 4, 5]) - even = toHashSet([2, 4, 6, 8]) - numbers.excl(even) - #echo numbers - # --> {1, 3, 5} - - block toSeqAndString: - var a = toHashSet([2, 7, 5]) - var b = initHashSet[int](rightSize(a.len)) - for x in [2, 7, 5]: b.incl(x) - assert($a == $b) - #echo a - #echo toHashSet(["no", "esc'aping", "is \" provided"]) - - #block orderedToSeqAndString: - # echo toOrderedSet([2, 4, 5]) - # echo toOrderedSet(["no", "esc'aping", "is \" provided"]) - - block setOperations: - var - a = toHashSet(["a", "b"]) - b = toHashSet(["b", "c"]) - c = union(a, b) - assert c == toHashSet(["a", "b", "c"]) - var d = intersection(a, b) - assert d == toHashSet(["b"]) - var e = difference(a, b) - assert e == toHashSet(["a"]) - var f = symmetricDifference(a, b) - assert f == toHashSet(["a", "c"]) - assert d < a and d < b - assert((a < a) == false) - assert d <= a and d <= b - assert((a <= a)) - # Alias test. - assert a + b == toHashSet(["a", "b", "c"]) - assert a * b == toHashSet(["b"]) - assert a - b == toHashSet(["a"]) - assert a -+- b == toHashSet(["a", "c"]) - assert disjoint(a, b) == false - assert disjoint(a, b - a) == true - - block mapSet: - var a = toHashSet([1, 2, 3]) - var b = a.map(proc (x: int): string = $x) - assert b == toHashSet(["1", "2", "3"]) - - block lenTest: - var values: OrderedSet[int] - assert values.len == 0 - assert values.card == 0 - - block setIterator: - type pair = tuple[a, b: int] - var a, b = initOrderedSet[pair]() - a.incl((2, 3)) - a.incl((3, 2)) - a.incl((2, 3)) - for x, y in a.items: - b.incl((x - 2, y + 1)) - assert a.len == b.card - assert a.len == 2 - - block setPairsIterator: - var s = toOrderedSet([1, 3, 5, 7]) - var items = newSeq[tuple[a: int, b: int]]() - for idx, item in s: items.add((idx, item)) - assert items == @[(0, 1), (1, 3), (2, 5), (3, 7)] - - block exclusions: - var s = toOrderedSet([1, 2, 3, 6, 7, 4]) - - s.excl(3) - s.excl(3) - s.excl(1) - s.excl(4) - - var items = newSeq[int]() - for item in s: items.add item - assert items == @[2, 6, 7] - - block: #9005 - var s = initOrderedSet[(int, int)]() - for i in 0 .. 30: incl(s, (i, 0)) - for i in 0 .. 30: excl(s, (i, 0)) - doAssert s.len == 0 - - #block orderedSetIterator: - # var a = initOrderedSet[int]() - # for value in [9, 2, 1, 5, 1, 8, 4, 2]: - # a.incl(value) - # for value in a.items: - # echo "Got ", value - - block setContains: - var values = initOrderedSet[int]() - assert(not values.contains(2)) - values.incl(2) - assert values.contains(2) - - block toSeqAndString: - var a = toOrderedSet([2, 4, 5]) - var b = initOrderedSet[int]() - for x in [2, 4, 5]: b.incl(x) - assert($a == $b) - assert(a == b) # https://github.com/Araq/Nim/issues/1413 - - block initBlocks: - var a: OrderedSet[int] - a.init(4) - a.incl(2) - a.init - assert a.len == 0 - a = initOrderedSet[int](4) - a.incl(2) - assert a.len == 1 - - var b: HashSet[int] - b.init(4) - b.incl(2) - b.init - assert b.len == 0 - b = initHashSet[int](4) - b.incl(2) - assert b.len == 1 - - block: - type FakeTable = object - dataLen: int - counter: int - countDeleted: int - - var t: FakeTable - for i in 0 .. 32: - var s = rightSize(i) - t.dataLen = s - t.counter = i - doAssert s > i and not mustRehash(t), - "performance issue: rightSize() will not elide enlarge() at: " & $i - - block missingOrExcl: - var s = toOrderedSet([2, 3, 6, 7]) - assert s.missingOrExcl(4) == true - assert s.missingOrExcl(6) == false - - block orderedSetEquality: - type pair = tuple[a, b: int] - - var aa = initOrderedSet[pair]() - var bb = initOrderedSet[pair]() - - var x = (a: 1, b: 2) - var y = (a: 3, b: 4) - - aa.incl(x) - aa.incl(y) - - bb.incl(x) - bb.incl(y) - assert aa == bb - - block setsWithoutInit: - var - a: HashSet[int] - b: HashSet[int] - c: HashSet[int] - d: HashSet[int] - e: HashSet[int] - - doAssert a.containsOrIncl(3) == false - doAssert a.contains(3) - doAssert a.len == 1 - doAssert a.containsOrIncl(3) - a.incl(3) - doAssert a.len == 1 - a.incl(6) - doAssert a.len == 2 - - b.incl(5) - doAssert b.len == 1 - b.excl(5) - b.excl(c) - doAssert b.missingOrExcl(5) - doAssert b.disjoint(c) - - d = b + c - doAssert d.len == 0 - d = b * c - doAssert d.len == 0 - d = b - c - doAssert d.len == 0 - d = b -+- c - doAssert d.len == 0 - - doAssert (d < e) == false - doAssert d <= e - doAssert d == e - - block setsWithoutInit: - var - a: OrderedSet[int] - b: OrderedSet[int] - c: OrderedSet[int] - d: HashSet[int] - - - doAssert a.containsOrIncl(3) == false - doAssert a.contains(3) - doAssert a.len == 1 - doAssert a.containsOrIncl(3) - a.incl(3) - doAssert a.len == 1 - a.incl(6) - doAssert a.len == 2 - - b.incl(5) - doAssert b.len == 1 - doAssert b.missingOrExcl(5) == false - doAssert b.missingOrExcl(5) - - doAssert c.missingOrExcl(9) - d.incl(c) - doAssert d.len == 0 - - when not defined(testing): - echo "Micro tests run successfully." - - testModule() + assert(len(s) == length, "the length of the OrderedSet changed while iterating over it") diff --git a/lib/pure/collections/sharedlist.nim b/lib/pure/collections/sharedlist.nim index 790529b79..ec8f1cd86 100644 --- a/lib/pure/collections/sharedlist.nim +++ b/lib/pure/collections/sharedlist.nim @@ -11,10 +11,12 @@ ## ## Unstable API. +{.deprecated.} + {.push stackTrace: off.} import - locks + std/locks const ElemsPerNode = 100 @@ -35,8 +37,10 @@ template withLock(t, x: untyped) = release(t.lock) proc iterAndMutate*[A](x: var SharedList[A]; action: proc(x: A): bool) = - ## iterates over the list. If 'action' returns true, the + ## Iterates over the list. If `action` returns true, the ## current item is removed from the list. + ## + ## .. warning:: It may not preserve the element order after some modifications. withLock(x): var n = x.head while n != nil: @@ -47,8 +51,9 @@ proc iterAndMutate*[A](x: var SharedList[A]; action: proc(x: A): bool) = if action(n.d[i]): acquire(x.lock) let t = x.tail + dec t.dataLen # TODO considering t.dataLen == 0, + # probably the module should be refactored using doubly linked lists n.d[i] = t.d[t.dataLen] - dec t.dataLen else: acquire(x.lock) inc i @@ -65,11 +70,14 @@ iterator items*[A](x: var SharedList[A]): A = proc add*[A](x: var SharedList[A]; y: A) = withLock(x): var node: SharedListNode[A] - if x.tail == nil or x.tail.dataLen == ElemsPerNode: - node = cast[type node](allocShared0(sizeof(node[]))) - node.next = x.tail + if x.tail == nil: + node = cast[typeof node](allocShared0(sizeof(node[]))) + x.tail = node + x.head = node + elif x.tail.dataLen == ElemsPerNode: + node = cast[typeof node](allocShared0(sizeof(node[]))) + x.tail.next = node x.tail = node - if x.head == nil: x.head = node else: node = x.tail node.d[node.dataLen] = y diff --git a/lib/pure/collections/sharedtables.nim b/lib/pure/collections/sharedtables.nim index abef89507..b474ecd31 100644 --- a/lib/pure/collections/sharedtables.nim +++ b/lib/pure/collections/sharedtables.nim @@ -14,8 +14,10 @@ ## ## Unstable API. +{.deprecated.} + import - hashes, math, locks + std/[hashes, math, locks] type KeyValuePair[A, B] = tuple[hcode: Hash, key: A, val: B] @@ -60,17 +62,27 @@ template withLock(t, x: untyped) = template withValue*[A, B](t: var SharedTable[A, B], key: A, value, body: untyped) = - ## retrieves the value at ``t[key]``. - ## `value` can be modified in the scope of the ``withValue`` call. - ## - ## .. code-block:: nim - ## - ## sharedTable.withValue(key, value) do: - ## # block is executed only if ``key`` in ``t`` - ## # value is threadsafe in block - ## value.name = "username" - ## value.uid = 1000 - ## + ## Retrieves the value at `t[key]`. + ## `value` can be modified in the scope of the `withValue` call. + runnableExamples: + var table: SharedTable[string, string] + init(table) + + table["a"] = "x" + table["b"] = "y" + table["c"] = "z" + + table.withValue("a", value): + assert value[] == "x" + + table.withValue("b", value): + value[] = "modified" + + table.withValue("b", value): + assert value[] == "modified" + + table.withValue("nonexistent", value): + assert false # not called acquire(t.lock) try: var hc: Hash @@ -84,20 +96,33 @@ template withValue*[A, B](t: var SharedTable[A, B], key: A, template withValue*[A, B](t: var SharedTable[A, B], key: A, value, body1, body2: untyped) = - ## retrieves the value at ``t[key]``. - ## `value` can be modified in the scope of the ``withValue`` call. - ## - ## .. code-block:: nim - ## - ## sharedTable.withValue(key, value) do: - ## # block is executed only if ``key`` in ``t`` - ## # value is threadsafe in block - ## value.name = "username" - ## value.uid = 1000 - ## do: - ## # block is executed when ``key`` not in ``t`` - ## raise newException(KeyError, "Key not found") - ## + ## Retrieves the value at `t[key]`. + ## `value` can be modified in the scope of the `withValue` call. + runnableExamples: + var table: SharedTable[string, string] + init(table) + + table["a"] = "x" + table["b"] = "y" + table["c"] = "z" + + + table.withValue("a", value): + value[] = "m" + + var flag = false + table.withValue("d", value): + discard value + doAssert false + do: # if "d" notin table + flag = true + + if flag: + table["d"] = "n" + + assert table.mget("a") == "m" + assert table.mget("d") == "n" + acquire(t.lock) try: var hc: Hash @@ -112,8 +137,8 @@ template withValue*[A, B](t: var SharedTable[A, B], key: A, release(t.lock) proc mget*[A, B](t: var SharedTable[A, B], key: A): var B = - ## retrieves the value at ``t[key]``. The value can be modified. - ## If `key` is not in `t`, the ``KeyError`` exception is raised. + ## Retrieves the value at `t[key]`. The value can be modified. + ## If `key` is not in `t`, the `KeyError` exception is raised. withLock t: var hc: Hash var index = rawGet(t, key, hc) @@ -126,44 +151,47 @@ proc mget*[A, B](t: var SharedTable[A, B], key: A): var B = raise newException(KeyError, "key not found") proc mgetOrPut*[A, B](t: var SharedTable[A, B], key: A, val: B): var B = - ## retrieves value at ``t[key]`` or puts ``val`` if not present, either way + ## Retrieves value at `t[key]` or puts `val` if not present, either way ## returning a value which can be modified. **Note**: This is inherently ## unsafe in the context of multi-threading since it returns a pointer - ## to ``B``. + ## to `B`. withLock t: mgetOrPutImpl(enlarge) proc hasKeyOrPut*[A, B](t: var SharedTable[A, B], key: A, val: B): bool = - ## returns true if `key` is in the table, otherwise inserts `value`. + ## Returns true if `key` is in the table, otherwise inserts `value`. withLock t: hasKeyOrPutImpl(enlarge) +template tabMakeEmpty(i) = t.data[i].hcode = 0 +template tabCellEmpty(i) = isEmpty(t.data[i].hcode) +template tabCellHash(i) = t.data[i].hcode + proc withKey*[A, B](t: var SharedTable[A, B], key: A, mapper: proc(key: A, val: var B, pairExists: var bool)) = - ## Computes a new mapping for the ``key`` with the specified ``mapper`` + ## Computes a new mapping for the `key` with the specified `mapper` ## procedure. ## - ## The ``mapper`` takes 3 arguments: + ## The `mapper` takes 3 arguments: ## - ## 1. ``key`` - the current key, if it exists, or the key passed to - ## ``withKey`` otherwise; - ## 2. ``val`` - the current value, if the key exists, or default value + ## 1. `key` - the current key, if it exists, or the key passed to + ## `withKey` otherwise; + ## 2. `val` - the current value, if the key exists, or default value ## of the type otherwise; - ## 3. ``pairExists`` - ``true`` if the key exists, ``false`` otherwise. + ## 3. `pairExists` - `true` if the key exists, `false` otherwise. ## - ## The ``mapper`` can can modify ``val`` and ``pairExists`` values to change + ## The `mapper` can can modify `val` and `pairExists` values to change ## the mapping of the key or delete it from the table. - ## When adding a value, make sure to set ``pairExists`` to ``true`` along - ## with modifying the ``val``. + ## When adding a value, make sure to set `pairExists` to `true` along + ## with modifying the `val`. ## ## The operation is performed atomically and other operations on the table - ## will be blocked while the ``mapper`` is invoked, so it should be short and + ## will be blocked while the `mapper` is invoked, so it should be short and ## simple. ## ## Example usage: ## - ## .. code-block:: nim - ## + ## ```nim ## # If value exists, decrement it. ## # If it becomes zero or less, delete the key ## t.withKey(1'i64) do (k: int64, v: var int, pairExists: var bool): @@ -171,6 +199,7 @@ proc withKey*[A, B](t: var SharedTable[A, B], key: A, ## dec v ## if v <= 0: ## pairExists = false + ## ``` withLock t: var hc: Hash var index = rawGet(t, key, hc) @@ -179,7 +208,7 @@ proc withKey*[A, B](t: var SharedTable[A, B], key: A, if pairExists: mapper(t.data[index].key, t.data[index].val, pairExists) if not pairExists: - delImplIdx(t, index) + delImplIdx(t, index, tabMakeEmpty, tabCellEmpty, tabCellHash) else: var val: B mapper(key, val, pairExists) @@ -187,35 +216,31 @@ proc withKey*[A, B](t: var SharedTable[A, B], key: A, st_maybeRehashPutImpl(enlarge) proc `[]=`*[A, B](t: var SharedTable[A, B], key: A, val: B) = - ## puts a (key, value)-pair into `t`. + ## Puts a (key, value)-pair into `t`. withLock t: putImpl(enlarge) proc add*[A, B](t: var SharedTable[A, B], key: A, val: B) = - ## puts a new (key, value)-pair into `t` even if ``t[key]`` already exists. + ## Puts a new (key, value)-pair into `t` even if `t[key]` already exists. ## This can introduce duplicate keys into the table! withLock t: addImpl(enlarge) proc del*[A, B](t: var SharedTable[A, B], key: A) = - ## deletes `key` from hash table `t`. + ## Deletes `key` from hash table `t`. withLock t: - delImpl() + delImpl(tabMakeEmpty, tabCellEmpty, tabCellHash) proc len*[A, B](t: var SharedTable[A, B]): int = - ## number of elements in `t` + ## Number of elements in `t`. withLock t: result = t.counter -proc init*[A, B](t: var SharedTable[A, B], initialSize = 64) = - ## creates a new hash table that is empty. +proc init*[A, B](t: var SharedTable[A, B], initialSize = 32) = + ## Creates a new hash table that is empty. ## ## This proc must be called before any other usage of `t`. - ## - ## `initialSize` needs to be a power of two. If you need to accept runtime - ## values for this you could use the ``nextPowerOfTwo`` proc from the - ## `math <math.html>`_ module or the ``rightSize`` proc from this module. - assert isPowerOfTwo(initialSize) + let initialSize = slotsNeeded(initialSize) t.counter = 0 t.dataLen = initialSize t.data = cast[KeyValuePairSeq[A, B]](allocShared0( diff --git a/lib/pure/collections/tableimpl.nim b/lib/pure/collections/tableimpl.nim index 47c14af93..3542741fa 100644 --- a/lib/pure/collections/tableimpl.nim +++ b/lib/pure/collections/tableimpl.nim @@ -7,10 +7,13 @@ # distribution, for details about the copyright. # -# An ``include`` file for the different table implementations. +# An `include` file for the different table implementations. include hashcommon +const + defaultInitialSize* = 32 + template rawGetDeepImpl() {.dirty.} = # Search algo for unconditional add genHashImpl(key, hc) var h: Hash = hc and maxHash(t) @@ -23,17 +26,16 @@ template rawInsertImpl() {.dirty.} = data[h].val = val data[h].hcode = hc -proc rawGetDeep[X, A](t: X, key: A, hc: var Hash): int {.inline.} = +proc rawGetDeep[X, A](t: X, key: A, hc: var Hash): int {.inline, outParamsAt: [3].} = rawGetDeepImpl() proc rawInsert[X, A, B](t: var X, data: var KeyValuePairSeq[A, B], - key: A, val: B, hc: Hash, h: Hash) = + key: A, val: sink B, hc: Hash, h: Hash) = rawInsertImpl() template checkIfInitialized() = - when compiles(defaultInitialSize): - if t.dataLen == 0: - initImpl(t, defaultInitialSize) + if t.dataLen == 0: + initImpl(t, defaultInitialSize) template addImpl(enlarge) {.dirty.} = checkIfInitialized() @@ -43,7 +45,7 @@ template addImpl(enlarge) {.dirty.} = rawInsert(t, t.data, key, val, hc, j) inc(t.counter) -template maybeRehashPutImpl(enlarge) {.dirty.} = +template maybeRehashPutImpl(enlarge, val) {.dirty.} = checkIfInitialized() if mustRehash(t): enlarge(t) @@ -54,46 +56,91 @@ template maybeRehashPutImpl(enlarge) {.dirty.} = template putImpl(enlarge) {.dirty.} = checkIfInitialized() - var hc: Hash + var hc: Hash = default(Hash) var index = rawGet(t, key, hc) if index >= 0: t.data[index].val = val - else: maybeRehashPutImpl(enlarge) + else: maybeRehashPutImpl(enlarge, val) template mgetOrPutImpl(enlarge) {.dirty.} = checkIfInitialized() - var hc: Hash + var hc: Hash = default(Hash) var index = rawGet(t, key, hc) if index < 0: # not present: insert (flipping index) - maybeRehashPutImpl(enlarge) + when declared(val): + maybeRehashPutImpl(enlarge, val) + else: + maybeRehashPutImpl(enlarge, default(B)) # either way return modifiable val result = t.data[index].val +# template mgetOrPutDefaultImpl(enlarge) {.dirty.} = +# checkIfInitialized() +# var hc: Hash = default(Hash) +# var index = rawGet(t, key, hc) +# if index < 0: +# # not present: insert (flipping index) +# maybeRehashPutImpl(enlarge, default(B)) +# # either way return modifiable val +# result = t.data[index].val + template hasKeyOrPutImpl(enlarge) {.dirty.} = checkIfInitialized() - var hc: Hash + var hc: Hash = default(Hash) var index = rawGet(t, key, hc) if index < 0: result = false - maybeRehashPutImpl(enlarge) + maybeRehashPutImpl(enlarge, val) else: result = true -template delImplIdx(t, i) = +# delImplIdx is KnuthV3 Algo6.4R adapted to i=i+1 (from i=i-1) which has come to +# be called "back shift delete". It shifts elements in the collision cluster of +# a victim backward to make things as-if the victim were never inserted in the +# first place. This is desirable to keep things "ageless" after many deletes. +# It is trickier than you might guess since initial probe (aka "home") locations +# of keys in a cluster may collide and since table addresses wrap around. +# +# A before-after diagram might look like ('.' means empty): +# slot: 0 1 2 3 4 5 6 7 +# before(1) +# hash1: 6 7 . 3 . 5 5 6 ; Really hash() and msk +# data1: E F . A . B C D ; About to delete C @index 6 +# after(2) +# hash2: 7 . . 3 . 5 6 6 ; Really hash() and msk +# data2: F . . A . B D E ; After deletion of C +# +# This lowers total search depth over the whole table from 1+1+2+2+2+2=10 to 7. +# Had the victim been B@5, C would need back shifting to slot 5. Total depth is +# always lowered by at least 1, e.g. victim A@3. This is all quite fast when +# empty slots are frequent (also needed to keep insert/miss searches fast) and +# hash() is either fast or avoided (via `.hcode`). It need not compare keys. +# +# delImplIdx realizes the above transformation, but only works for dense Linear +# Probing, nextTry(h)=h+1. This is not an important limitation since that's the +# fastest sequence on any CPU made since the 1980s. { Performance analysis often +# overweights "key cmp" neglecting cache behavior, giving bad ideas how big/slow +# tables behave (when perf matters most!). Comparing hcode first means usually +# only 1 key cmp is needed for *any* seq. Timing only predictable activity, +# small tables, and/or integer keys often perpetuates such bad ideas. } + +template delImplIdx(t, i, makeEmpty, cellEmpty, cellHash) = let msk = maxHash(t) if i >= 0: dec(t.counter) block outer: while true: # KnuthV3 Algo6.4R adapted for i=i+1 instead of i=i-1 - var j = i # The correctness of this depends on (h+1) in nextTry, + var j = i # The correctness of this depends on (h+1) in nextTry var r = j # though may be adaptable to other simple sequences. - t.data[i].hcode = 0 # mark current EMPTY - t.data[i].key = default(type(t.data[i].key)) - t.data[i].val = default(type(t.data[i].val)) + makeEmpty(i) # mark current EMPTY + {.push warning[UnsafeDefault]:off.} + reset(t.data[i].key) + reset(t.data[i].val) + {.pop.} while true: i = (i + 1) and msk # increment mod table size - if isEmpty(t.data[i].hcode): # end of collision cluster; So all done + if cellEmpty(i): # end of collision cluster; So all done break outer - r = t.data[i].hcode and msk # "home" location of key@i + r = cellHash(i) and msk # initial probe index for key@slot i if not ((i >= r and r > j) or (r > j and j > i) or (j > i and i >= r)): break when defined(js): @@ -101,17 +148,28 @@ template delImplIdx(t, i) = else: t.data[j] = move(t.data[i]) # data[j] will be marked EMPTY next loop -template delImpl() {.dirty.} = +template delImpl(makeEmpty, cellEmpty, cellHash) {.dirty.} = var hc: Hash var i = rawGet(t, key, hc) - delImplIdx(t, i) + delImplIdx(t, i, makeEmpty, cellEmpty, cellHash) + +template delImplNoHCode(makeEmpty, cellEmpty, cellHash) {.dirty.} = + if t.dataLen > 0: + var i: Hash = hash(key) and maxHash(t) + while not cellEmpty(i): + if t.data[i].key == key: + delImplIdx(t, i, makeEmpty, cellEmpty, cellHash) + break + i = nextTry(i, maxHash(t)) template clearImpl() {.dirty.} = for i in 0 ..< t.dataLen: when compiles(t.data[i].hcode): # CountTable records don't contain a hcode t.data[i].hcode = 0 - t.data[i].key = default(type(t.data[i].key)) - t.data[i].val = default(type(t.data[i].val)) + {.push warning[UnsafeDefault]:off.} + reset(t.data[i].key) + reset(t.data[i].val) + {.pop.} t.counter = 0 template ctAnd(a, b): bool = @@ -121,12 +179,12 @@ template ctAnd(a, b): bool = else: false template initImpl(result: typed, size: int) = - when ctAnd(declared(SharedTable), type(result) is SharedTable): - init(result, size) + let correctSize = slotsNeeded(size) + when ctAnd(declared(SharedTable), typeof(result) is SharedTable): + init(result, correctSize) else: - assert isPowerOfTwo(size) result.counter = 0 - newSeq(result.data, size) + newSeq(result.data, correctSize) when compiles(result.first): result.first = -1 result.last = -1 @@ -169,3 +227,5 @@ template equalsImpl(s, t: typed) = if not t.hasKey(key): return false if t.getOrDefault(key) != val: return false return true + else: + return false diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index a969a4c5d..d414caeed 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -7,219 +7,198 @@ # distribution, for details about the copyright. # -## The ``tables`` module implements variants of an efficient `hash table`:idx: +## The `tables` module implements variants of an efficient `hash table`:idx: ## (also often named `dictionary`:idx: in other programming languages) that is ## a mapping from keys to values. ## ## There are several different types of hash tables available: ## * `Table<#Table>`_ is the usual hash table, -## * `OrderedTable<#OrderedTable>`_ is like ``Table`` but remembers insertion order, +## * `OrderedTable<#OrderedTable>`_ is like `Table` but remembers insertion order, ## * `CountTable<#CountTable>`_ is a mapping from a key to its number of occurrences ## ## For consistency with every other data type in Nim these have **value** -## semantics, this means that ``=`` performs a copy of the hash table. +## semantics, this means that `=` performs a copy of the hash table. ## ## For `ref semantics<manual.html#types-reference-and-pointer-types>`_ -## use their ``Ref`` variants: `TableRef<#TableRef>`_, +## use their `Ref` variants: `TableRef<#TableRef>`_, ## `OrderedTableRef<#OrderedTableRef>`_, and `CountTableRef<#CountTableRef>`_. ## -## To give an example, when ``a`` is a ``Table``, then ``var b = a`` gives ``b`` -## as a new independent table. ``b`` is initialised with the contents of ``a``. -## Changing ``b`` does not affect ``a`` and vice versa: -## -## .. code-block:: -## import tables -## -## var -## a = {1: "one", 2: "two"}.toTable # creates a Table -## b = a -## -## echo a, b # output: {1: one, 2: two}{1: one, 2: two} -## -## b[3] = "three" -## echo a, b # output: {1: one, 2: two}{1: one, 2: two, 3: three} -## echo a == b # output: false -## -## On the other hand, when ``a`` is a ``TableRef`` instead, then changes to ``b`` -## also affect ``a``. Both ``a`` and ``b`` **ref** the same data structure: -## -## .. code-block:: -## import tables -## -## var -## a = {1: "one", 2: "two"}.newTable # creates a TableRef -## b = a -## -## echo a, b # output: {1: one, 2: two}{1: one, 2: two} -## -## b[3] = "three" -## echo a, b # output: {1: one, 2: two, 3: three}{1: one, 2: two, 3: three} -## echo a == b # output: true +## To give an example, when `a` is a `Table`, then `var b = a` gives `b` +## as a new independent table. `b` is initialised with the contents of `a`. +## Changing `b` does not affect `a` and vice versa: + +runnableExamples: + var + a = {1: "one", 2: "two"}.toTable # creates a Table + b = a + + assert a == b + + b[3] = "three" + assert 3 notin a + assert 3 in b + assert a != b + +## On the other hand, when `a` is a `TableRef` instead, then changes to `b` +## also affect `a`. Both `a` and `b` **ref** the same data structure: + +runnableExamples: + var + a = {1: "one", 2: "two"}.newTable # creates a TableRef + b = a + + assert a == b + + b[3] = "three" + + assert 3 in a + assert 3 in b + assert a == b + ## ## ---- ## -## Basic usage -## =========== -## -## Table -## ----- -## -## .. code-block:: -## import tables -## from sequtils import zip -## -## let -## names = ["John", "Paul", "George", "Ringo"] -## years = [1940, 1942, 1943, 1940] -## -## var beatles = initTable[string, int]() -## -## for pairs in zip(names, years): -## let (name, birthYear) = pairs -## beatles[name] = birthYear -## -## echo beatles -## # {"George": 1943, "Ringo": 1940, "Paul": 1942, "John": 1940} -## -## -## var beatlesByYear = initTable[int, seq[string]]() -## -## for pairs in zip(years, names): -## let (birthYear, name) = pairs -## if not beatlesByYear.hasKey(birthYear): -## # if a key doesn't exist, we create one with an empty sequence -## # before we can add elements to it -## beatlesByYear[birthYear] = @[] -## beatlesByYear[birthYear].add(name) -## -## echo beatlesByYear -## # {1940: @["John", "Ringo"], 1942: @["Paul"], 1943: @["George"]} -## -## -## -## OrderedTable -## ------------ -## + +## # Basic usage + + +## ## Table +runnableExamples: + from std/sequtils import zip + + let + names = ["John", "Paul", "George", "Ringo"] + years = [1940, 1942, 1943, 1940] + + var beatles = initTable[string, int]() + + for pairs in zip(names, years): + let (name, birthYear) = pairs + beatles[name] = birthYear + + assert beatles == {"George": 1943, "Ringo": 1940, "Paul": 1942, "John": 1940}.toTable + + + var beatlesByYear = initTable[int, seq[string]]() + + for pairs in zip(years, names): + let (birthYear, name) = pairs + if not beatlesByYear.hasKey(birthYear): + # if a key doesn't exist, we create one with an empty sequence + # before we can add elements to it + beatlesByYear[birthYear] = @[] + beatlesByYear[birthYear].add(name) + + assert beatlesByYear == {1940: @["John", "Ringo"], 1942: @["Paul"], 1943: @["George"]}.toTable + +## ## OrderedTable ## `OrderedTable<#OrderedTable>`_ is used when it is important to preserve ## the insertion order of keys. -## -## .. code-block:: -## import tables -## -## let -## a = [('z', 1), ('y', 2), ('x', 3)] -## t = a.toTable # regular table -## ot = a.toOrderedTable # ordered tables -## -## echo t # {'x': 3, 'y': 2, 'z': 1} -## echo ot # {'z': 1, 'y': 2, 'x': 3} -## -## -## -## CountTable -## ---------- -## + +runnableExamples: + let + a = [('z', 1), ('y', 2), ('x', 3)] + ot = a.toOrderedTable # ordered tables + + assert $ot == """{'z': 1, 'y': 2, 'x': 3}""" + +## ## CountTable ## `CountTable<#CountTable>`_ is useful for counting number of items of some ## container (e.g. string, sequence or array), as it is a mapping where the ## items are the keys, and their number of occurrences are the values. ## For that purpose `toCountTable proc<#toCountTable,openArray[A]>`_ ## comes handy: -## -## .. code-block:: -## import tables -## -## let myString = "abracadabra" -## let letterFrequencies = toCountTable(myString) -## echo letterFrequencies -## # 'a': 5, 'b': 2, 'c': 1, 'd': 1, 'r': 2} -## + +runnableExamples: + let myString = "abracadabra" + let letterFrequencies = toCountTable(myString) + assert $letterFrequencies == "{'a': 5, 'd': 1, 'b': 2, 'r': 2, 'c': 1}" + ## The same could have been achieved by manually iterating over a container ## and increasing each key's value with `inc proc -## <#inc,CountTable[A],A,Positive>`_: -## -## .. code-block:: -## import tables -## -## let myString = "abracadabra" -## var letterFrequencies = initCountTable[char]() -## for c in myString: -## letterFrequencies.inc(c) -## echo letterFrequencies -## # output: {'a': 5, 'b': 2, 'c': 1, 'd': 1, 'r': 2} +## <#inc,CountTable[A],A,int>`_: + +runnableExamples: + let myString = "abracadabra" + var letterFrequencies = initCountTable[char]() + for c in myString: + letterFrequencies.inc(c) + assert $letterFrequencies == "{'d': 1, 'r': 2, 'c': 1, 'a': 5, 'b': 2}" + ## ## ---- ## + +## ## Hashing ## -## -## Hashing -## ------- -## -## If you are using simple standard types like ``int`` or ``string`` for the +## If you are using simple standard types like `int` or `string` for the ## keys of the table you won't have any problems, but as soon as you try to use ## a more complex object as a key you will be greeted by a strange compiler ## error: ## -## Error: type mismatch: got (Person) -## but expected one of: -## hashes.hash(x: openArray[A]): Hash -## hashes.hash(x: int): Hash -## hashes.hash(x: float): Hash -## … +## Error: type mismatch: got (Person) +## but expected one of: +## hashes.hash(x: openArray[A]): Hash +## hashes.hash(x: int): Hash +## hashes.hash(x: float): Hash ## ## What is happening here is that the types used for table keys require to have -## a ``hash()`` proc which will convert them to a `Hash <hashes.html#Hash>`_ +## a `hash()` proc which will convert them to a `Hash <hashes.html#Hash>`_ ## value, and the compiler is listing all the hash functions it knows. -## Additionally there has to be a ``==`` operator that provides the same -## semantics as its corresponding ``hash`` proc. +## Additionally there has to be a `==` operator that provides the same +## semantics as its corresponding `hash` proc. ## -## After you add ``hash`` and ``==`` for your custom type everything will work. -## Currently, however, ``hash`` for objects is not defined, whereas -## ``system.==`` for objects does exist and performs a "deep" comparison (every +## After you add `hash` and `==` for your custom type everything will work. +## Currently, however, `hash` for objects is not defined, whereas +## `system.==` for objects does exist and performs a "deep" comparison (every ## field is compared) which is usually what you want. So in the following -## example implementing only ``hash`` suffices: -## -## .. code-block:: -## import tables, hashes -## -## type -## Person = object -## firstName, lastName: string -## -## proc hash(x: Person): Hash = -## ## Piggyback on the already available string hash proc. -## ## -## ## Without this proc nothing works! -## result = x.firstName.hash !& x.lastName.hash -## result = !$result -## -## var -## salaries = initTable[Person, int]() -## p1, p2: Person -## -## p1.firstName = "Jon" -## p1.lastName = "Ross" -## salaries[p1] = 30_000 -## -## p2.firstName = "소진" -## p2.lastName = "박" -## salaries[p2] = 45_000 +## example implementing only `hash` suffices: + +runnableExamples: + import std/hashes + + type + Person = object + firstName, lastName: string + + proc hash(x: Person): Hash = + ## Piggyback on the already available string hash proc. + ## + ## Without this proc nothing works! + result = x.firstName.hash !& x.lastName.hash + result = !$result + + var + salaries = initTable[Person, int]() + p1, p2: Person + + p1.firstName = "Jon" + p1.lastName = "Ross" + salaries[p1] = 30_000 + + p2.firstName = "소진" + p2.lastName = "박" + salaries[p2] = 45_000 + ## ## ---- ## -## See also -## ======== + +## # See also ## ## * `json module<json.html>`_ for table-like structure which allows ## heterogeneous members -## * `sharedtables module<sharedtables.html>`_ for shared hash table support ## * `strtabs module<strtabs.html>`_ for efficient hash tables ## mapping from strings to strings ## * `hashes module<hashes.html>`_ for helper functions for hashing import std/private/since +import std/[hashes, math, algorithm] + -import hashes, math, algorithm +when not defined(nimHasEffectsOf): + {.pragma: effectsOf.} type KeyValuePair[A, B] = tuple[hcode: Hash, key: A, val: B] @@ -230,16 +209,14 @@ type ## `data` and `counter` are internal implementation details which ## can't be accessed. ## - ## For creating an empty Table, use `initTable proc<#initTable,int>`_. + ## For creating an empty Table, use `initTable proc<#initTable>`_. data: KeyValuePairSeq[A, B] counter: int TableRef*[A, B] = ref Table[A, B] ## Ref version of `Table<#Table>`_. ## ## For creating a new empty TableRef, use `newTable proc - ## <#newTable,int>`_. + ## <#newTable>`_. -const - defaultInitialSize* = 64 # ------------------------------ helpers --------------------------------- @@ -250,18 +227,21 @@ template dataLen(t): untyped = len(t.data) include tableimpl +proc raiseKeyError[T](key: T) {.noinline, noreturn.} = + when compiles($key): + raise newException(KeyError, "key not found: " & $key) + else: + raise newException(KeyError, "key not found") + template get(t, key): untyped = - ## retrieves the value at ``t[key]``. The value can be modified. - ## If ``key`` is not in ``t``, the ``KeyError`` exception is raised. + ## retrieves the value at `t[key]`. The value can be modified. + ## If `key` is not in `t`, the `KeyError` exception is raised. mixin rawGet var hc: Hash var index = rawGet(t, key, hc) if index >= 0: result = t.data[index].val else: - when compiles($key): - raise newException(KeyError, "key not found: " & $key) - else: - raise newException(KeyError, "key not found") + raiseKeyError(key) proc enlarge[A, B](t: var Table[A, B]) = var n: KeyValuePairSeq[A, B] @@ -288,26 +268,21 @@ proc enlarge[A, B](t: var Table[A, B]) = proc initTable*[A, B](initialSize = defaultInitialSize): Table[A, B] = ## Creates a new hash table that is empty. ## - ## ``initialSize`` must be a power of two (default: 64). - ## If you need to accept runtime values for this you could use the - ## `nextPowerOfTwo proc<math.html#nextPowerOfTwo,int>`_ from the - ## `math module<math.html>`_ or the `rightSize proc<#rightSize,Natural>`_ - ## from this module. - ## ## Starting from Nim v0.20, tables are initialized by default and it is ## not necessary to call this function explicitly. ## ## See also: ## * `toTable proc<#toTable,openArray[]>`_ - ## * `newTable proc<#newTable,int>`_ for creating a `TableRef` + ## * `newTable proc<#newTable>`_ for creating a `TableRef` runnableExamples: let a = initTable[int, string]() b = initTable[char, seq[int]]() + result = default(Table[A, B]) initImpl(result, initialSize) -proc `[]=`*[A, B](t: var Table[A, B], key: A, val: B) = - ## Inserts a ``(key, value)`` pair into ``t``. +proc `[]=`*[A, B](t: var Table[A, B], key: A, val: sink B) = + ## Inserts a `(key, value)` pair into `t`. ## ## See also: ## * `[] proc<#[],Table[A,B],A>`_ for retrieving a value of a key @@ -323,25 +298,25 @@ proc `[]=`*[A, B](t: var Table[A, B], key: A, val: B) = putImpl(enlarge) proc toTable*[A, B](pairs: openArray[(A, B)]): Table[A, B] = - ## Creates a new hash table that contains the given ``pairs``. + ## Creates a new hash table that contains the given `pairs`. ## - ## ``pairs`` is a container consisting of ``(key, value)`` tuples. + ## `pairs` is a container consisting of `(key, value)` tuples. ## ## See also: - ## * `initTable proc<#initTable,int>`_ + ## * `initTable proc<#initTable>`_ ## * `newTable proc<#newTable,openArray[]>`_ for a `TableRef` version runnableExamples: let a = [('a', 5), ('b', 9)] let b = toTable(a) assert b == {'a': 5, 'b': 9}.toTable - result = initTable[A, B](rightSize(pairs.len)) + result = initTable[A, B](pairs.len) for key, val in items(pairs): result[key] = val -proc `[]`*[A, B](t: Table[A, B], key: A): B = - ## Retrieves the value at ``t[key]``. +proc `[]`*[A, B](t: Table[A, B], key: A): lent B = + ## Retrieves the value at `t[key]`. ## - ## If ``key`` is not in ``t``, the ``KeyError`` exception is raised. + ## If `key` is not in `t`, the `KeyError` exception is raised. ## One can check with `hasKey proc<#hasKey,Table[A,B],A>`_ whether ## the key exists. ## @@ -350,7 +325,7 @@ proc `[]`*[A, B](t: Table[A, B], key: A): B = ## a default value (e.g. zero for int) if the key doesn't exist ## * `getOrDefault proc<#getOrDefault,Table[A,B],A,B>`_ to return ## a custom value if the key doesn't exist - ## * `[]= proc<#[]=,Table[A,B],A,B>`_ for inserting a new + ## * `[]= proc<#[]=,Table[A,B],A,sinkB>`_ for inserting a new ## (key, value) pair in the table ## * `hasKey proc<#hasKey,Table[A,B],A>`_ for checking if a key is in ## the table @@ -362,23 +337,23 @@ proc `[]`*[A, B](t: Table[A, B], key: A): B = get(t, key) proc `[]`*[A, B](t: var Table[A, B], key: A): var B = - ## Retrieves the value at ``t[key]``. The value can be modified. + ## Retrieves the value at `t[key]`. The value can be modified. ## - ## If ``key`` is not in ``t``, the ``KeyError`` exception is raised. + ## If `key` is not in `t`, the `KeyError` exception is raised. ## ## See also: ## * `getOrDefault proc<#getOrDefault,Table[A,B],A>`_ to return ## a default value (e.g. zero for int) if the key doesn't exist ## * `getOrDefault proc<#getOrDefault,Table[A,B],A,B>`_ to return ## a custom value if the key doesn't exist - ## * `[]= proc<#[]=,Table[A,B],A,B>`_ for inserting a new + ## * `[]= proc<#[]=,Table[A,B],A,sinkB>`_ for inserting a new ## (key, value) pair in the table ## * `hasKey proc<#hasKey,Table[A,B],A>`_ for checking if a key is in ## the table get(t, key) proc hasKey*[A, B](t: Table[A, B], key: A): bool = - ## Returns true if ``key`` is in the table ``t``. + ## Returns true if `key` is in the table `t`. ## ## See also: ## * `contains proc<#contains,Table[A,B],A>`_ for use with the `in` operator @@ -397,7 +372,7 @@ proc hasKey*[A, B](t: Table[A, B], key: A): bool = proc contains*[A, B](t: Table[A, B], key: A): bool = ## Alias of `hasKey proc<#hasKey,Table[A,B],A>`_ for use with - ## the ``in`` operator. + ## the `in` operator. runnableExamples: let a = {'a': 5, 'b': 9}.toTable doAssert 'b' in a == true @@ -406,7 +381,7 @@ proc contains*[A, B](t: Table[A, B], key: A): bool = return hasKey[A, B](t, key) proc hasKeyOrPut*[A, B](t: var Table[A, B], key: A, val: B): bool = - ## Returns true if ``key`` is in the table, otherwise inserts ``value``. + ## Returns true if `key` is in the table, otherwise inserts `value`. ## ## See also: ## * `hasKey proc<#hasKey,Table[A,B],A>`_ @@ -426,8 +401,8 @@ proc hasKeyOrPut*[A, B](t: var Table[A, B], key: A, val: B): bool = hasKeyOrPutImpl(enlarge) proc getOrDefault*[A, B](t: Table[A, B], key: A): B = - ## Retrieves the value at ``t[key]`` if ``key`` is in ``t``. Otherwise, the - ## default initialization value for type ``B`` is returned (e.g. 0 for any + ## Retrieves the value at `t[key]` if `key` is in `t`. Otherwise, the + ## default initialization value for type `B` is returned (e.g. 0 for any ## integer type). ## ## See also: @@ -441,12 +416,12 @@ proc getOrDefault*[A, B](t: Table[A, B], key: A): B = let a = {'a': 5, 'b': 9}.toTable doAssert a.getOrDefault('a') == 5 doAssert a.getOrDefault('z') == 0 - + result = default(B) getOrDefaultImpl(t, key) proc getOrDefault*[A, B](t: Table[A, B], key: A, default: B): B = - ## Retrieves the value at ``t[key]`` if ``key`` is in ``t``. - ## Otherwise, ``default`` is returned. + ## Retrieves the value at `t[key]` if `key` is in `t`. + ## Otherwise, `default` is returned. ## ## See also: ## * `[] proc<#[],Table[A,B],A>`_ for retrieving a value of a key @@ -459,13 +434,20 @@ proc getOrDefault*[A, B](t: Table[A, B], key: A, default: B): B = let a = {'a': 5, 'b': 9}.toTable doAssert a.getOrDefault('a', 99) == 5 doAssert a.getOrDefault('z', 99) == 99 - + result = default(B) getOrDefaultImpl(t, key, default) proc mgetOrPut*[A, B](t: var Table[A, B], key: A, val: B): var B = - ## Retrieves value at ``t[key]`` or puts ``val`` if not present, either way + ## Retrieves value at `t[key]` or puts `val` if not present, either way ## returning a value which can be modified. ## + ## + ## Note that while the value returned is of type `var B`, + ## it is easy to accidentally create a copy of the value at `t[key]`. + ## Remember that seqs and strings are value types, and therefore + ## cannot be copied into a separate variable for modification. + ## See the example below. + ## ## See also: ## * `[] proc<#[],Table[A,B],A>`_ for retrieving a value of a key ## * `hasKey proc<#hasKey,Table[A,B],A>`_ @@ -480,27 +462,58 @@ proc mgetOrPut*[A, B](t: var Table[A, B], key: A, val: B): var B = doAssert a.mgetOrPut('z', 99) == 99 doAssert a == {'a': 5, 'b': 9, 'z': 99}.toTable + # An example of accidentally creating a copy + var t = initTable[int, seq[int]]() + # In this example, we expect t[10] to be modified, + # but it is not. + var copiedSeq = t.mgetOrPut(10, @[10]) + copiedSeq.add(20) + doAssert t[10] == @[10] + # Correct + t.mgetOrPut(25, @[25]).add(35) + doAssert t[25] == @[25, 35] + + mgetOrPutImpl(enlarge) + +proc mgetOrPut*[A, B](t: var Table[A, B], key: A): var B = + ## Retrieves the value at `t[key]` or puts the + ## default initialization value for type `B` (e.g. 0 for any + ## integer type). + runnableExamples: + var a = {'a': 5}.newTable + doAssert a.mgetOrPut('a') == 5 + a.mgetOrPut('z').inc + doAssert a == {'a': 5, 'z': 1}.newTable + mgetOrPutImpl(enlarge) proc len*[A, B](t: Table[A, B]): int = - ## Returns the number of keys in ``t``. + ## Returns the number of keys in `t`. runnableExamples: let a = {'a': 5, 'b': 9}.toTable doAssert len(a) == 2 result = t.counter -proc add*[A, B](t: var Table[A, B], key: A, val: B) = - ## Puts a new ``(key, value)`` pair into ``t`` even if ``t[key]`` already exists. +proc add*[A, B](t: var Table[A, B], key: A, val: sink B) {.deprecated: + "Deprecated since v1.4; it was more confusing than useful, use `[]=`".} = + ## Puts a new `(key, value)` pair into `t` even if `t[key]` already exists. ## ## **This can introduce duplicate keys into the table!** ## - ## Use `[]= proc<#[]=,Table[A,B],A,B>`_ for inserting a new + ## Use `[]= proc<#[]=,Table[A,B],A,sinkB>`_ for inserting a new ## (key, value) pair in the table without introducing duplicates. addImpl(enlarge) +template tabMakeEmpty(i) = t.data[i].hcode = 0 +template tabCellEmpty(i) = isEmpty(t.data[i].hcode) +template tabCellHash(i) = t.data[i].hcode + proc del*[A, B](t: var Table[A, B], key: A) = - ## Deletes ``key`` from hash table ``t``. Does nothing if the key does not exist. + ## Deletes `key` from hash table `t`. Does nothing if the key does not exist. + ## + ## .. warning:: If duplicate keys were added (via the now deprecated `add` proc), + ## this may need to be called multiple times. ## ## See also: ## * `pop proc<#pop,Table[A,B],A,B>`_ @@ -512,14 +525,17 @@ proc del*[A, B](t: var Table[A, B], key: A) = a.del('z') doAssert a == {'b': 9, 'c': 13}.toTable - delImpl() + delImpl(tabMakeEmpty, tabCellEmpty, tabCellHash) proc pop*[A, B](t: var Table[A, B], key: A, val: var B): bool = - ## Deletes the ``key`` from the table. - ## Returns ``true``, if the ``key`` existed, and sets ``val`` to the - ## mapping of the key. Otherwise, returns ``false``, and the ``val`` is + ## Deletes the `key` from the table. + ## Returns `true`, if the `key` existed, and sets `val` to the + ## mapping of the key. Otherwise, returns `false`, and the `val` is ## unchanged. ## + ## .. warning:: If duplicate keys were added (via the now deprecated `add` proc), + ## this may need to be called multiple times. + ## ## See also: ## * `del proc<#del,Table[A,B],A>`_ ## * `clear proc<#clear,Table[A,B]>`_ to empty the whole table @@ -540,7 +556,7 @@ proc pop*[A, B](t: var Table[A, B], key: A, val: var B): bool = result = index >= 0 if result: val = move(t.data[index].val) - delImplIdx(t, index) + delImplIdx(t, index, tabMakeEmpty, tabCellEmpty, tabCellHash) proc take*[A, B](t: var Table[A, B], key: A, val: var B): bool {.inline.} = ## Alias for: @@ -562,12 +578,12 @@ proc clear*[A, B](t: var Table[A, B]) = clearImpl() proc `$`*[A, B](t: Table[A, B]): string = - ## The ``$`` operator for hash tables. Used internally when calling `echo` + ## The `$` operator for hash tables. Used internally when calling `echo` ## on a table. dollarImpl() proc `==`*[A, B](s, t: Table[A, B]): bool = - ## The ``==`` operator for hash tables. Returns ``true`` if the content of both + ## The `==` operator for hash tables. Returns `true` if the content of both ## tables contains the same key-value pairs. Insert order does not matter. runnableExamples: let @@ -587,17 +603,31 @@ proc indexBy*[A, B, C](collection: A, index: proc(x: B): C): Table[C, B] = template withValue*[A, B](t: var Table[A, B], key: A, value, body: untyped) = - ## Retrieves the value at ``t[key]``. - ## - ## ``value`` can be modified in the scope of the ``withValue`` call. - ## - ## .. code-block:: nim - ## - ## sharedTable.withValue(key, value) do: - ## # block is executed only if ``key`` in ``t`` - ## value.name = "username" - ## value.uid = 1000 + ## Retrieves the value at `t[key]`. ## + ## `value` can be modified in the scope of the `withValue` call. + runnableExamples: + type + User = object + name: string + uid: int + + var t = initTable[int, User]() + let u = User(name: "Hello", uid: 99) + t[1] = u + + t.withValue(1, value): + # block is executed only if `key` in `t` + value.name = "Nim" + value.uid = 1314 + + t.withValue(2, value): + value.name = "No" + value.uid = 521 + + assert t[1].name == "Nim" + assert t[1].uid == 1314 + mixin rawGet var hc: Hash var index = rawGet(t, key, hc) @@ -608,20 +638,35 @@ template withValue*[A, B](t: var Table[A, B], key: A, value, body: untyped) = template withValue*[A, B](t: var Table[A, B], key: A, value, body1, body2: untyped) = - ## Retrieves the value at ``t[key]``. - ## - ## ``value`` can be modified in the scope of the ``withValue`` call. - ## - ## .. code-block:: nim - ## - ## table.withValue(key, value) do: - ## # block is executed only if ``key`` in ``t`` - ## value.name = "username" - ## value.uid = 1000 - ## do: - ## # block is executed when ``key`` not in ``t`` - ## raise newException(KeyError, "Key not found") + ## Retrieves the value at `t[key]`. ## + ## `value` can be modified in the scope of the `withValue` call. + runnableExamples: + type + User = object + name: string + uid: int + + var t = initTable[int, User]() + let u = User(name: "Hello", uid: 99) + t[1] = u + + t.withValue(1, value): + # block is executed only if `key` in `t` + value.name = "Nim" + value.uid = 1314 + + t.withValue(521, value): + doAssert false + do: + # block is executed when `key` not in `t` + t[1314] = User(name: "exist", uid: 521) + + assert t[1].name == "Nim" + assert t[1].uid == 1314 + assert t[1314].name == "exist" + assert t[1314].uid == 521 + mixin rawGet var hc: Hash var index = rawGet(t, key, hc) @@ -634,7 +679,7 @@ template withValue*[A, B](t: var Table[A, B], key: A, iterator pairs*[A, B](t: Table[A, B]): (A, B) = - ## Iterates over any ``(key, value)`` pair in the table ``t``. + ## Iterates over any `(key, value)` pair in the table `t`. ## ## See also: ## * `mpairs iterator<#mpairs.i,Table[A,B]>`_ @@ -643,7 +688,7 @@ iterator pairs*[A, B](t: Table[A, B]): (A, B) = ## ## **Examples:** ## - ## .. code-block:: + ## ```Nim ## let a = { ## 'o': [1, 5, 7, 9], ## 'e': [2, 4, 6, 8] @@ -657,6 +702,7 @@ iterator pairs*[A, B](t: Table[A, B]): (A, B) = ## # value: [2, 4, 6, 8] ## # key: o ## # value: [1, 5, 7, 9] + ## ``` let L = len(t) for h in 0 .. high(t.data): if isFilled(t.data[h].hcode): @@ -664,7 +710,7 @@ iterator pairs*[A, B](t: Table[A, B]): (A, B) = assert(len(t) == L, "the length of the table changed while iterating over it") iterator mpairs*[A, B](t: var Table[A, B]): (A, var B) = - ## Iterates over any ``(key, value)`` pair in the table ``t`` (must be + ## Iterates over any `(key, value)` pair in the table `t` (must be ## declared as `var`). The values can be modified. ## ## See also: @@ -685,8 +731,8 @@ iterator mpairs*[A, B](t: var Table[A, B]): (A, var B) = yield (t.data[h].key, t.data[h].val) assert(len(t) == L, "the length of the table changed while iterating over it") -iterator keys*[A, B](t: Table[A, B]): A = - ## Iterates over any key in the table ``t``. +iterator keys*[A, B](t: Table[A, B]): lent A = + ## Iterates over any key in the table `t`. ## ## See also: ## * `pairs iterator<#pairs.i,Table[A,B]>`_ @@ -706,8 +752,8 @@ iterator keys*[A, B](t: Table[A, B]): A = yield t.data[h].key assert(len(t) == L, "the length of the table changed while iterating over it") -iterator values*[A, B](t: Table[A, B]): B = - ## Iterates over any value in the table ``t``. +iterator values*[A, B](t: Table[A, B]): lent B = + ## Iterates over any value in the table `t`. ## ## See also: ## * `pairs iterator<#pairs.i,Table[A,B]>`_ @@ -728,7 +774,7 @@ iterator values*[A, B](t: Table[A, B]): B = assert(len(t) == L, "the length of the table changed while iterating over it") iterator mvalues*[A, B](t: var Table[A, B]): var B = - ## Iterates over any value in the table ``t`` (must be + ## Iterates over any value in the table `t` (must be ## declared as `var`). The values can be modified. ## ## See also: @@ -749,14 +795,15 @@ iterator mvalues*[A, B](t: var Table[A, B]): var B = yield t.data[h].val assert(len(t) == L, "the length of the table changed while iterating over it") -iterator allValues*[A, B](t: Table[A, B]; key: A): B = - ## Iterates over any value in the table ``t`` that belongs to the given ``key``. +iterator allValues*[A, B](t: Table[A, B]; key: A): B {.deprecated: + "Deprecated since v1.4; tables with duplicated keys are deprecated".} = + ## Iterates over any value in the table `t` that belongs to the given `key`. ## ## Used if you have a table with duplicate keys (as a result of using - ## `add proc<#add,Table[A,B],A,B>`_). + ## `add proc<#add,Table[A,B],A,sinkB>`_). ## runnableExamples: - import sequtils, algorithm + import std/[sequtils, algorithm] var a = {'a': 3, 'b': 5}.toTable for i in 1..3: a.add('z', 10*i) @@ -777,34 +824,29 @@ iterator allValues*[A, B](t: Table[A, B]; key: A): B = # ------------------------------------------------------------------- -proc newTable*[A, B](initialSize = defaultInitialSize): <//>TableRef[A, B] = +proc newTable*[A, B](initialSize = defaultInitialSize): TableRef[A, B] = ## Creates a new ref hash table that is empty. ## - ## ``initialSize`` must be a power of two (default: 64). - ## If you need to accept runtime values for this you could use the - ## `nextPowerOfTwo proc<math.html#nextPowerOfTwo,int>`_ from the - ## `math module<math.html>`_ or the `rightSize proc<#rightSize,Natural>`_ - ## from this module. - ## ## See also: ## * `newTable proc<#newTable,openArray[]>`_ for creating a `TableRef` ## from a collection of `(key, value)` pairs - ## * `initTable proc<#initTable,int>`_ for creating a `Table` + ## * `initTable proc<#initTable>`_ for creating a `Table` runnableExamples: let a = newTable[int, string]() b = newTable[char, seq[int]]() new(result) - result[] = initTable[A, B](initialSize) + {.noSideEffect.}: + result[] = initTable[A, B](initialSize) -proc newTable*[A, B](pairs: openArray[(A, B)]): <//>TableRef[A, B] = - ## Creates a new ref hash table that contains the given ``pairs``. +proc newTable*[A, B](pairs: openArray[(A, B)]): TableRef[A, B] = + ## Creates a new ref hash table that contains the given `pairs`. ## - ## ``pairs`` is a container consisting of ``(key, value)`` tuples. + ## `pairs` is a container consisting of `(key, value)` tuples. ## ## See also: - ## * `newTable proc<#newTable,int>`_ + ## * `newTable proc<#newTable>`_ ## * `toTable proc<#toTable,openArray[]>`_ for a `Table` version runnableExamples: let a = [('a', 5), ('b', 9)] @@ -812,19 +854,21 @@ proc newTable*[A, B](pairs: openArray[(A, B)]): <//>TableRef[A, B] = assert b == {'a': 5, 'b': 9}.newTable new(result) - result[] = toTable[A, B](pairs) + {.noSideEffect.}: + result[] = toTable[A, B](pairs) -proc newTableFrom*[A, B, C](collection: A, index: proc(x: B): C): <//>TableRef[C, B] = +proc newTableFrom*[A, B, C](collection: A, index: proc(x: B): C): TableRef[C, B] = ## Index the collection with the proc provided. # TODO: As soon as supported, change collection: A to collection: A[B] result = newTable[C, B]() - for item in collection: - result[index(item)] = item + {.noSideEffect.}: + for item in collection: + result[index(item)] = item proc `[]`*[A, B](t: TableRef[A, B], key: A): var B = - ## Retrieves the value at ``t[key]``. + ## Retrieves the value at `t[key]`. ## - ## If ``key`` is not in ``t``, the ``KeyError`` exception is raised. + ## If `key` is not in `t`, the `KeyError` exception is raised. ## One can check with `hasKey proc<#hasKey,TableRef[A,B],A>`_ whether ## the key exists. ## @@ -833,7 +877,7 @@ proc `[]`*[A, B](t: TableRef[A, B], key: A): var B = ## a default value (e.g. zero for int) if the key doesn't exist ## * `getOrDefault proc<#getOrDefault,TableRef[A,B],A,B>`_ to return ## a custom value if the key doesn't exist - ## * `[]= proc<#[]=,TableRef[A,B],A,B>`_ for inserting a new + ## * `[]= proc<#[]=,TableRef[A,B],A,sinkB>`_ for inserting a new ## (key, value) pair in the table ## * `hasKey proc<#hasKey,TableRef[A,B],A>`_ for checking if a key is in ## the table @@ -845,8 +889,8 @@ proc `[]`*[A, B](t: TableRef[A, B], key: A): var B = result = t[][key] -proc `[]=`*[A, B](t: TableRef[A, B], key: A, val: B) = - ## Inserts a ``(key, value)`` pair into ``t``. +proc `[]=`*[A, B](t: TableRef[A, B], key: A, val: sink B) = + ## Inserts a `(key, value)` pair into `t`. ## ## See also: ## * `[] proc<#[],TableRef[A,B],A>`_ for retrieving a value of a key @@ -862,7 +906,7 @@ proc `[]=`*[A, B](t: TableRef[A, B], key: A, val: B) = t[][key] = val proc hasKey*[A, B](t: TableRef[A, B], key: A): bool = - ## Returns true if ``key`` is in the table ``t``. + ## Returns true if `key` is in the table `t`. ## ## See also: ## * `contains proc<#contains,TableRef[A,B],A>`_ for use with the `in` @@ -881,7 +925,7 @@ proc hasKey*[A, B](t: TableRef[A, B], key: A): bool = proc contains*[A, B](t: TableRef[A, B], key: A): bool = ## Alias of `hasKey proc<#hasKey,TableRef[A,B],A>`_ for use with - ## the ``in`` operator. + ## the `in` operator. runnableExamples: let a = {'a': 5, 'b': 9}.newTable doAssert 'b' in a == true @@ -889,8 +933,8 @@ proc contains*[A, B](t: TableRef[A, B], key: A): bool = return hasKey[A, B](t, key) -proc hasKeyOrPut*[A, B](t: var TableRef[A, B], key: A, val: B): bool = - ## Returns true if ``key`` is in the table, otherwise inserts ``value``. +proc hasKeyOrPut*[A, B](t: TableRef[A, B], key: A, val: B): bool = + ## Returns true if `key` is in the table, otherwise inserts `value`. ## ## See also: ## * `hasKey proc<#hasKey,TableRef[A,B],A>`_ @@ -910,8 +954,8 @@ proc hasKeyOrPut*[A, B](t: var TableRef[A, B], key: A, val: B): bool = t[].hasKeyOrPut(key, val) proc getOrDefault*[A, B](t: TableRef[A, B], key: A): B = - ## Retrieves the value at ``t[key]`` if ``key`` is in ``t``. Otherwise, the - ## default initialization value for type ``B`` is returned (e.g. 0 for any + ## Retrieves the value at `t[key]` if `key` is in `t`. Otherwise, the + ## default initialization value for type `B` is returned (e.g. 0 for any ## integer type). ## ## See also: @@ -929,8 +973,8 @@ proc getOrDefault*[A, B](t: TableRef[A, B], key: A): B = getOrDefault(t[], key) proc getOrDefault*[A, B](t: TableRef[A, B], key: A, default: B): B = - ## Retrieves the value at ``t[key]`` if ``key`` is in ``t``. - ## Otherwise, ``default`` is returned. + ## Retrieves the value at `t[key]` if `key` is in `t`. + ## Otherwise, `default` is returned. ## ## See also: ## * `[] proc<#[],TableRef[A,B],A>`_ for retrieving a value of a key @@ -947,9 +991,15 @@ proc getOrDefault*[A, B](t: TableRef[A, B], key: A, default: B): B = getOrDefault(t[], key, default) proc mgetOrPut*[A, B](t: TableRef[A, B], key: A, val: B): var B = - ## Retrieves value at ``t[key]`` or puts ``val`` if not present, either way + ## Retrieves value at `t[key]` or puts `val` if not present, either way ## returning a value which can be modified. ## + ## Note that while the value returned is of type `var B`, + ## it is easy to accidentally create an copy of the value at `t[key]`. + ## Remember that seqs and strings are value types, and therefore + ## cannot be copied into a separate variable for modification. + ## See the example below. + ## ## See also: ## * `[] proc<#[],TableRef[A,B],A>`_ for retrieving a value of a key ## * `hasKey proc<#hasKey,TableRef[A,B],A>`_ @@ -964,29 +1014,53 @@ proc mgetOrPut*[A, B](t: TableRef[A, B], key: A, val: B): var B = doAssert a.mgetOrPut('z', 99) == 99 doAssert a == {'a': 5, 'b': 9, 'z': 99}.newTable + # An example of accidentally creating a copy + var t = newTable[int, seq[int]]() + # In this example, we expect t[10] to be modified, + # but it is not. + var copiedSeq = t.mgetOrPut(10, @[10]) + copiedSeq.add(20) + doAssert t[10] == @[10] + # Correct + t.mgetOrPut(25, @[25]).add(35) + doAssert t[25] == @[25, 35] t[].mgetOrPut(key, val) +proc mgetOrPut*[A, B](t: TableRef[A, B], key: A): var B = + ## Retrieves the value at `t[key]` or puts the + ## default initialization value for type `B` (e.g. 0 for any + ## integer type). + runnableExamples: + var a = {'a': 5}.newTable + doAssert a.mgetOrPut('a') == 5 + a.mgetOrPut('z').inc + doAssert a == {'a': 5, 'z': 1}.newTable + + t[].mgetOrPut(key) + proc len*[A, B](t: TableRef[A, B]): int = - ## Returns the number of keys in ``t``. + ## Returns the number of keys in `t`. runnableExamples: let a = {'a': 5, 'b': 9}.newTable doAssert len(a) == 2 result = t.counter -proc add*[A, B](t: TableRef[A, B], key: A, val: B) = - ## Puts a new ``(key, value)`` pair into ``t`` even if ``t[key]`` already exists. +proc add*[A, B](t: TableRef[A, B], key: A, val: sink B) {.deprecated: + "Deprecated since v1.4; it was more confusing than useful, use `[]=`".} = + ## Puts a new `(key, value)` pair into `t` even if `t[key]` already exists. ## ## **This can introduce duplicate keys into the table!** ## - ## Use `[]= proc<#[]=,TableRef[A,B],A,B>`_ for inserting a new + ## Use `[]= proc<#[]=,TableRef[A,B],A,sinkB>`_ for inserting a new ## (key, value) pair in the table without introducing duplicates. t[].add(key, val) proc del*[A, B](t: TableRef[A, B], key: A) = - ## Deletes ``key`` from hash table ``t``. Does nothing if the key does not exist. + ## Deletes `key` from hash table `t`. Does nothing if the key does not exist. ## - ## **If duplicate keys were added, this may need to be called multiple times.** + ## .. warning:: If duplicate keys were added (via the now deprecated `add` proc), + ## this may need to be called multiple times. ## ## See also: ## * `pop proc<#pop,TableRef[A,B],A,B>`_ @@ -1001,12 +1075,13 @@ proc del*[A, B](t: TableRef[A, B], key: A) = t[].del(key) proc pop*[A, B](t: TableRef[A, B], key: A, val: var B): bool = - ## Deletes the ``key`` from the table. - ## Returns ``true``, if the ``key`` existed, and sets ``val`` to the - ## mapping of the key. Otherwise, returns ``false``, and the ``val`` is + ## Deletes the `key` from the table. + ## Returns `true`, if the `key` existed, and sets `val` to the + ## mapping of the key. Otherwise, returns `false`, and the `val` is ## unchanged. ## - ## **If duplicate keys were added, this may need to be called multiple times.** + ## .. warning:: If duplicate keys were added (via the now deprecated `add` proc), + ## this may need to be called multiple times. ## ## See also: ## * `del proc<#del,TableRef[A,B],A>`_ @@ -1045,13 +1120,13 @@ proc clear*[A, B](t: TableRef[A, B]) = clearImpl() proc `$`*[A, B](t: TableRef[A, B]): string = - ## The ``$`` operator for hash tables. Used internally when calling `echo` + ## The `$` operator for hash tables. Used internally when calling `echo` ## on a table. dollarImpl() proc `==`*[A, B](s, t: TableRef[A, B]): bool = - ## The ``==`` operator for hash tables. Returns ``true`` if either both tables - ## are ``nil``, or neither is ``nil`` and the content of both tables contains the + ## The `==` operator for hash tables. Returns `true` if either both tables + ## are `nil`, or neither is `nil` and the content of both tables contains the ## same key-value pairs. Insert order does not matter. runnableExamples: let @@ -1066,7 +1141,7 @@ proc `==`*[A, B](s, t: TableRef[A, B]): bool = iterator pairs*[A, B](t: TableRef[A, B]): (A, B) = - ## Iterates over any ``(key, value)`` pair in the table ``t``. + ## Iterates over any `(key, value)` pair in the table `t`. ## ## See also: ## * `mpairs iterator<#mpairs.i,TableRef[A,B]>`_ @@ -1075,7 +1150,7 @@ iterator pairs*[A, B](t: TableRef[A, B]): (A, B) = ## ## **Examples:** ## - ## .. code-block:: + ## ```Nim ## let a = { ## 'o': [1, 5, 7, 9], ## 'e': [2, 4, 6, 8] @@ -1089,6 +1164,7 @@ iterator pairs*[A, B](t: TableRef[A, B]): (A, B) = ## # value: [2, 4, 6, 8] ## # key: o ## # value: [1, 5, 7, 9] + ## ``` let L = len(t) for h in 0 .. high(t.data): if isFilled(t.data[h].hcode): @@ -1096,7 +1172,7 @@ iterator pairs*[A, B](t: TableRef[A, B]): (A, B) = assert(len(t) == L, "the length of the table changed while iterating over it") iterator mpairs*[A, B](t: TableRef[A, B]): (A, var B) = - ## Iterates over any ``(key, value)`` pair in the table ``t``. The values + ## Iterates over any `(key, value)` pair in the table `t`. The values ## can be modified. ## ## See also: @@ -1117,8 +1193,8 @@ iterator mpairs*[A, B](t: TableRef[A, B]): (A, var B) = yield (t.data[h].key, t.data[h].val) assert(len(t) == L, "the length of the table changed while iterating over it") -iterator keys*[A, B](t: TableRef[A, B]): A = - ## Iterates over any key in the table ``t``. +iterator keys*[A, B](t: TableRef[A, B]): lent A = + ## Iterates over any key in the table `t`. ## ## See also: ## * `pairs iterator<#pairs.i,TableRef[A,B]>`_ @@ -1138,8 +1214,8 @@ iterator keys*[A, B](t: TableRef[A, B]): A = yield t.data[h].key assert(len(t) == L, "the length of the table changed while iterating over it") -iterator values*[A, B](t: TableRef[A, B]): B = - ## Iterates over any value in the table ``t``. +iterator values*[A, B](t: TableRef[A, B]): lent B = + ## Iterates over any value in the table `t`. ## ## See also: ## * `pairs iterator<#pairs.i,TableRef[A,B]>`_ @@ -1160,7 +1236,7 @@ iterator values*[A, B](t: TableRef[A, B]): B = assert(len(t) == L, "the length of the table changed while iterating over it") iterator mvalues*[A, B](t: TableRef[A, B]): var B = - ## Iterates over any value in the table ``t``. The values can be modified. + ## Iterates over any value in the table `t`. The values can be modified. ## ## See also: ## * `mpairs iterator<#mpairs.i,TableRef[A,B]>`_ @@ -1199,14 +1275,14 @@ type ## Hash table that remembers insertion order. ## ## For creating an empty OrderedTable, use `initOrderedTable proc - ## <#initOrderedTable,int>`_. + ## <#initOrderedTable>`_. data: OrderedKeyValuePairSeq[A, B] counter, first, last: int OrderedTableRef*[A, B] = ref OrderedTable[A, B] ## Ref version of ## `OrderedTable<#OrderedTable>`_. ## ## For creating a new empty OrderedTableRef, use `newOrderedTable proc - ## <#newOrderedTable,int>`_. + ## <#newOrderedTable>`_. # ------------------------------ helpers --------------------------------- @@ -1222,7 +1298,7 @@ proc rawGet[A, B](t: OrderedTable[A, B], key: A, hc: var Hash): int = proc rawInsert[A, B](t: var OrderedTable[A, B], data: var OrderedKeyValuePairSeq[A, B], - key: A, val: B, hc: Hash, h: Hash) = + key: A, val: sink B, hc: Hash, h: Hash) = rawInsertImpl() data[h].next = -1 if t.first < 0: t.first = h @@ -1260,27 +1336,22 @@ template forAllOrderedPairs(yieldStmt: untyped) {.dirty.} = proc initOrderedTable*[A, B](initialSize = defaultInitialSize): OrderedTable[A, B] = ## Creates a new ordered hash table that is empty. ## - ## ``initialSize`` must be a power of two (default: 64). - ## If you need to accept runtime values for this you could use the - ## `nextPowerOfTwo proc<math.html#nextPowerOfTwo,int>`_ from the - ## `math module<math.html>`_ or the `rightSize proc<#rightSize,Natural>`_ - ## from this module. - ## ## Starting from Nim v0.20, tables are initialized by default and it is ## not necessary to call this function explicitly. ## ## See also: ## * `toOrderedTable proc<#toOrderedTable,openArray[]>`_ - ## * `newOrderedTable proc<#newOrderedTable,int>`_ for creating an + ## * `newOrderedTable proc<#newOrderedTable>`_ for creating an ## `OrderedTableRef` runnableExamples: let a = initOrderedTable[int, string]() b = initOrderedTable[char, seq[int]]() + result = default(OrderedTable[A, B]) initImpl(result, initialSize) -proc `[]=`*[A, B](t: var OrderedTable[A, B], key: A, val: B) = - ## Inserts a ``(key, value)`` pair into ``t``. +proc `[]=`*[A, B](t: var OrderedTable[A, B], key: A, val: sink B) = + ## Inserts a `(key, value)` pair into `t`. ## ## See also: ## * `[] proc<#[],OrderedTable[A,B],A>`_ for retrieving a value of a key @@ -1296,12 +1367,12 @@ proc `[]=`*[A, B](t: var OrderedTable[A, B], key: A, val: B) = putImpl(enlarge) proc toOrderedTable*[A, B](pairs: openArray[(A, B)]): OrderedTable[A, B] = - ## Creates a new ordered hash table that contains the given ``pairs``. + ## Creates a new ordered hash table that contains the given `pairs`. ## - ## ``pairs`` is a container consisting of ``(key, value)`` tuples. + ## `pairs` is a container consisting of `(key, value)` tuples. ## ## See also: - ## * `initOrderedTable proc<#initOrderedTable,int>`_ + ## * `initOrderedTable proc<#initOrderedTable>`_ ## * `newOrderedTable proc<#newOrderedTable,openArray[]>`_ for an ## `OrderedTableRef` version runnableExamples: @@ -1309,13 +1380,13 @@ proc toOrderedTable*[A, B](pairs: openArray[(A, B)]): OrderedTable[A, B] = let b = toOrderedTable(a) assert b == {'a': 5, 'b': 9}.toOrderedTable - result = initOrderedTable[A, B](rightSize(pairs.len)) + result = initOrderedTable[A, B](pairs.len) for key, val in items(pairs): result[key] = val -proc `[]`*[A, B](t: OrderedTable[A, B], key: A): B = - ## Retrieves the value at ``t[key]``. +proc `[]`*[A, B](t: OrderedTable[A, B], key: A): lent B = + ## Retrieves the value at `t[key]`. ## - ## If ``key`` is not in ``t``, the ``KeyError`` exception is raised. + ## If `key` is not in `t`, the `KeyError` exception is raised. ## One can check with `hasKey proc<#hasKey,OrderedTable[A,B],A>`_ whether ## the key exists. ## @@ -1324,7 +1395,7 @@ proc `[]`*[A, B](t: OrderedTable[A, B], key: A): B = ## a default value (e.g. zero for int) if the key doesn't exist ## * `getOrDefault proc<#getOrDefault,OrderedTable[A,B],A,B>`_ to return ## a custom value if the key doesn't exist - ## * `[]= proc<#[]=,OrderedTable[A,B],A,B>`_ for inserting a new + ## * `[]= proc<#[]=,OrderedTable[A,B],A,sinkB>`_ for inserting a new ## (key, value) pair in the table ## * `hasKey proc<#hasKey,OrderedTable[A,B],A>`_ for checking if a ## key is in the table @@ -1337,23 +1408,23 @@ proc `[]`*[A, B](t: OrderedTable[A, B], key: A): B = get(t, key) proc `[]`*[A, B](t: var OrderedTable[A, B], key: A): var B = - ## Retrieves the value at ``t[key]``. The value can be modified. + ## Retrieves the value at `t[key]`. The value can be modified. ## - ## If ``key`` is not in ``t``, the ``KeyError`` exception is raised. + ## If `key` is not in `t`, the `KeyError` exception is raised. ## ## See also: ## * `getOrDefault proc<#getOrDefault,OrderedTable[A,B],A>`_ to return ## a default value (e.g. zero for int) if the key doesn't exist ## * `getOrDefault proc<#getOrDefault,OrderedTable[A,B],A,B>`_ to return ## a custom value if the key doesn't exist - ## * `[]= proc<#[]=,OrderedTable[A,B],A,B>`_ for inserting a new + ## * `[]= proc<#[]=,OrderedTable[A,B],A,sinkB>`_ for inserting a new ## (key, value) pair in the table ## * `hasKey proc<#hasKey,OrderedTable[A,B],A>`_ for checking if a ## key is in the table get(t, key) proc hasKey*[A, B](t: OrderedTable[A, B], key: A): bool = - ## Returns true if ``key`` is in the table ``t``. + ## Returns true if `key` is in the table `t`. ## ## See also: ## * `contains proc<#contains,OrderedTable[A,B],A>`_ for use with the `in` @@ -1368,12 +1439,12 @@ proc hasKey*[A, B](t: OrderedTable[A, B], key: A): bool = doAssert a.hasKey('a') == true doAssert a.hasKey('z') == false - var hc: Hash + var hc: Hash = default(Hash) result = rawGet(t, key, hc) >= 0 proc contains*[A, B](t: OrderedTable[A, B], key: A): bool = ## Alias of `hasKey proc<#hasKey,OrderedTable[A,B],A>`_ for use with - ## the ``in`` operator. + ## the `in` operator. runnableExamples: let a = {'a': 5, 'b': 9}.toOrderedTable doAssert 'b' in a == true @@ -1382,7 +1453,7 @@ proc contains*[A, B](t: OrderedTable[A, B], key: A): bool = return hasKey[A, B](t, key) proc hasKeyOrPut*[A, B](t: var OrderedTable[A, B], key: A, val: B): bool = - ## Returns true if ``key`` is in the table, otherwise inserts ``value``. + ## Returns true if `key` is in the table, otherwise inserts `value`. ## ## See also: ## * `hasKey proc<#hasKey,OrderedTable[A,B],A>`_ @@ -1402,8 +1473,8 @@ proc hasKeyOrPut*[A, B](t: var OrderedTable[A, B], key: A, val: B): bool = hasKeyOrPutImpl(enlarge) proc getOrDefault*[A, B](t: OrderedTable[A, B], key: A): B = - ## Retrieves the value at ``t[key]`` if ``key`` is in ``t``. Otherwise, the - ## default initialization value for type ``B`` is returned (e.g. 0 for any + ## Retrieves the value at `t[key]` if `key` is in `t`. Otherwise, the + ## default initialization value for type `B` is returned (e.g. 0 for any ## integer type). ## ## See also: @@ -1417,12 +1488,12 @@ proc getOrDefault*[A, B](t: OrderedTable[A, B], key: A): B = let a = {'a': 5, 'b': 9}.toOrderedTable doAssert a.getOrDefault('a') == 5 doAssert a.getOrDefault('z') == 0 - + result = default(B) getOrDefaultImpl(t, key) proc getOrDefault*[A, B](t: OrderedTable[A, B], key: A, default: B): B = - ## Retrieves the value at ``t[key]`` if ``key`` is in ``t``. - ## Otherwise, ``default`` is returned. + ## Retrieves the value at `t[key]` if `key` is in `t`. + ## Otherwise, `default` is returned. ## ## See also: ## * `[] proc<#[],OrderedTable[A,B],A>`_ for retrieving a value of a key @@ -1435,11 +1506,11 @@ proc getOrDefault*[A, B](t: OrderedTable[A, B], key: A, default: B): B = let a = {'a': 5, 'b': 9}.toOrderedTable doAssert a.getOrDefault('a', 99) == 5 doAssert a.getOrDefault('z', 99) == 99 - + result = default(B) getOrDefaultImpl(t, key, default) proc mgetOrPut*[A, B](t: var OrderedTable[A, B], key: A, val: B): var B = - ## Retrieves value at ``t[key]`` or puts ``val`` if not present, either way + ## Retrieves value at `t[key]` or puts `val` if not present, either way ## returning a value which can be modified. ## ## See also: @@ -1458,25 +1529,38 @@ proc mgetOrPut*[A, B](t: var OrderedTable[A, B], key: A, val: B): var B = mgetOrPutImpl(enlarge) +proc mgetOrPut*[A, B](t: var OrderedTable[A, B], key: A): var B = + ## Retrieves the value at `t[key]` or puts the + ## default initialization value for type `B` (e.g. 0 for any + ## integer type). + runnableExamples: + var a = {'a': 5}.toOrderedTable + doAssert a.mgetOrPut('a') == 5 + a.mgetOrPut('z').inc + doAssert a == {'a': 5, 'z': 1}.toOrderedTable + + mgetOrPutImpl(enlarge) + proc len*[A, B](t: OrderedTable[A, B]): int {.inline.} = - ## Returns the number of keys in ``t``. + ## Returns the number of keys in `t`. runnableExamples: let a = {'a': 5, 'b': 9}.toOrderedTable doAssert len(a) == 2 result = t.counter -proc add*[A, B](t: var OrderedTable[A, B], key: A, val: B) = - ## Puts a new ``(key, value)`` pair into ``t`` even if ``t[key]`` already exists. +proc add*[A, B](t: var OrderedTable[A, B], key: A, val: sink B) {.deprecated: + "Deprecated since v1.4; it was more confusing than useful, use `[]=`".} = + ## Puts a new `(key, value)` pair into `t` even if `t[key]` already exists. ## ## **This can introduce duplicate keys into the table!** ## - ## Use `[]= proc<#[]=,OrderedTable[A,B],A,B>`_ for inserting a new + ## Use `[]= proc<#[]=,OrderedTable[A,B],A,sinkB>`_ for inserting a new ## (key, value) pair in the table without introducing duplicates. addImpl(enlarge) proc del*[A, B](t: var OrderedTable[A, B], key: A) = - ## Deletes ``key`` from hash table ``t``. Does nothing if the key does not exist. + ## Deletes `key` from hash table `t`. Does nothing if the key does not exist. ## ## O(n) complexity. ## @@ -1509,9 +1593,9 @@ proc del*[A, B](t: var OrderedTable[A, B], key: A) = h = nxt proc pop*[A, B](t: var OrderedTable[A, B], key: A, val: var B): bool {.since: (1, 1).} = - ## Deletes the ``key`` from the table. - ## Returns ``true``, if the ``key`` existed, and sets ``val`` to the - ## mapping of the key. Otherwise, returns ``false``, and the ``val`` is + ## Deletes the `key` from the table. + ## Returns `true`, if the `key` existed, and sets `val` to the + ## mapping of the key. Otherwise, returns `false`, and the `val` is ## unchanged. ## ## O(n) complexity. @@ -1555,15 +1639,15 @@ proc clear*[A, B](t: var OrderedTable[A, B]) = t.last = -1 proc sort*[A, B](t: var OrderedTable[A, B], cmp: proc (x, y: (A, B)): int, - order = SortOrder.Ascending) = - ## Sorts ``t`` according to the function ``cmp``. + order = SortOrder.Ascending) {.effectsOf: cmp.} = + ## Sorts `t` according to the function `cmp`. ## ## This modifies the internal list ## that kept the insertion order, so insertion order is lost after this - ## call but key lookup and insertions remain possible after ``sort`` (in + ## call but key lookup and insertions remain possible after `sort` (in ## contrast to the `sort proc<#sort,CountTable[A]>`_ for count tables). runnableExamples: - import algorithm + import std/[algorithm] var a = initOrderedTable[char, int]() for i, c in "cab": a[c] = 10*i @@ -1614,12 +1698,12 @@ proc sort*[A, B](t: var OrderedTable[A, B], cmp: proc (x, y: (A, B)): int, t.last = tail proc `$`*[A, B](t: OrderedTable[A, B]): string = - ## The ``$`` operator for ordered hash tables. Used internally when calling + ## The `$` operator for ordered hash tables. Used internally when calling ## `echo` on a table. dollarImpl() proc `==`*[A, B](s, t: OrderedTable[A, B]): bool = - ## The ``==`` operator for ordered hash tables. Returns ``true`` if both the + ## The `==` operator for ordered hash tables. Returns `true` if both the ## content and the order are equal. runnableExamples: let @@ -1629,6 +1713,8 @@ proc `==`*[A, B](s, t: OrderedTable[A, B]): bool = if s.counter != t.counter: return false + if s.counter == 0 and t.counter == 0: + return true var ht = t.first var hs = s.first while ht >= 0 and hs >= 0: @@ -1644,7 +1730,7 @@ proc `==`*[A, B](s, t: OrderedTable[A, B]): bool = iterator pairs*[A, B](t: OrderedTable[A, B]): (A, B) = - ## Iterates over any ``(key, value)`` pair in the table ``t`` in insertion + ## Iterates over any `(key, value)` pair in the table `t` in insertion ## order. ## ## See also: @@ -1654,7 +1740,7 @@ iterator pairs*[A, B](t: OrderedTable[A, B]): (A, B) = ## ## **Examples:** ## - ## .. code-block:: + ## ```Nim ## let a = { ## 'o': [1, 5, 7, 9], ## 'e': [2, 4, 6, 8] @@ -1668,6 +1754,7 @@ iterator pairs*[A, B](t: OrderedTable[A, B]): (A, B) = ## # value: [1, 5, 7, 9] ## # key: e ## # value: [2, 4, 6, 8] + ## ``` let L = len(t) forAllOrderedPairs: @@ -1675,7 +1762,7 @@ iterator pairs*[A, B](t: OrderedTable[A, B]): (A, B) = assert(len(t) == L, "the length of the table changed while iterating over it") iterator mpairs*[A, B](t: var OrderedTable[A, B]): (A, var B) = - ## Iterates over any ``(key, value)`` pair in the table ``t`` (must be + ## Iterates over any `(key, value)` pair in the table `t` (must be ## declared as `var`) in insertion order. The values can be modified. ## ## See also: @@ -1696,8 +1783,8 @@ iterator mpairs*[A, B](t: var OrderedTable[A, B]): (A, var B) = yield (t.data[h].key, t.data[h].val) assert(len(t) == L, "the length of the table changed while iterating over it") -iterator keys*[A, B](t: OrderedTable[A, B]): A = - ## Iterates over any key in the table ``t`` in insertion order. +iterator keys*[A, B](t: OrderedTable[A, B]): lent A = + ## Iterates over any key in the table `t` in insertion order. ## ## See also: ## * `pairs iterator<#pairs.i,OrderedTable[A,B]>`_ @@ -1717,8 +1804,8 @@ iterator keys*[A, B](t: OrderedTable[A, B]): A = yield t.data[h].key assert(len(t) == L, "the length of the table changed while iterating over it") -iterator values*[A, B](t: OrderedTable[A, B]): B = - ## Iterates over any value in the table ``t`` in insertion order. +iterator values*[A, B](t: OrderedTable[A, B]): lent B = + ## Iterates over any value in the table `t` in insertion order. ## ## See also: ## * `pairs iterator<#pairs.i,OrderedTable[A,B]>`_ @@ -1738,7 +1825,7 @@ iterator values*[A, B](t: OrderedTable[A, B]): B = assert(len(t) == L, "the length of the table changed while iterating over it") iterator mvalues*[A, B](t: var OrderedTable[A, B]): var B = - ## Iterates over any value in the table ``t`` (must be + ## Iterates over any value in the table `t` (must be ## declared as `var`) in insertion order. The values ## can be modified. ## @@ -1760,42 +1847,33 @@ iterator mvalues*[A, B](t: var OrderedTable[A, B]): var B = yield t.data[h].val assert(len(t) == L, "the length of the table changed while iterating over it") - - - - # --------------------------------------------------------------------------- # --------------------------- OrderedTableRef ------------------------------- # --------------------------------------------------------------------------- -proc newOrderedTable*[A, B](initialSize = defaultInitialSize): <//>OrderedTableRef[A, B] = +proc newOrderedTable*[A, B](initialSize = defaultInitialSize): OrderedTableRef[A, B] = ## Creates a new ordered ref hash table that is empty. ## - ## ``initialSize`` must be a power of two (default: 64). - ## If you need to accept runtime values for this you could use the - ## `nextPowerOfTwo proc<math.html#nextPowerOfTwo,int>`_ from the - ## `math module<math.html>`_ or the `rightSize proc<#rightSize,Natural>`_ - ## from this module. - ## ## See also: ## * `newOrderedTable proc<#newOrderedTable,openArray[]>`_ for creating ## an `OrderedTableRef` from a collection of `(key, value)` pairs - ## * `initOrderedTable proc<#initOrderedTable,int>`_ for creating an + ## * `initOrderedTable proc<#initOrderedTable>`_ for creating an ## `OrderedTable` runnableExamples: let a = newOrderedTable[int, string]() b = newOrderedTable[char, seq[int]]() new(result) - result[] = initOrderedTable[A, B](initialSize) + {.noSideEffect.}: + result[] = initOrderedTable[A, B](initialSize) -proc newOrderedTable*[A, B](pairs: openArray[(A, B)]): <//>OrderedTableRef[A, B] = - ## Creates a new ordered ref hash table that contains the given ``pairs``. +proc newOrderedTable*[A, B](pairs: openArray[(A, B)]): OrderedTableRef[A, B] = + ## Creates a new ordered ref hash table that contains the given `pairs`. ## - ## ``pairs`` is a container consisting of ``(key, value)`` tuples. + ## `pairs` is a container consisting of `(key, value)` tuples. ## ## See also: - ## * `newOrderedTable proc<#newOrderedTable,int>`_ + ## * `newOrderedTable proc<#newOrderedTable>`_ ## * `toOrderedTable proc<#toOrderedTable,openArray[]>`_ for an ## `OrderedTable` version runnableExamples: @@ -1803,14 +1881,15 @@ proc newOrderedTable*[A, B](pairs: openArray[(A, B)]): <//>OrderedTableRef[A, B] let b = newOrderedTable(a) assert b == {'a': 5, 'b': 9}.newOrderedTable - result = newOrderedTable[A, B](rightSize(pairs.len)) - for key, val in items(pairs): result.add(key, val) + result = newOrderedTable[A, B](pairs.len) + {.noSideEffect.}: + for key, val in items(pairs): result[key] = val proc `[]`*[A, B](t: OrderedTableRef[A, B], key: A): var B = - ## Retrieves the value at ``t[key]``. + ## Retrieves the value at `t[key]`. ## - ## If ``key`` is not in ``t``, the ``KeyError`` exception is raised. + ## If `key` is not in `t`, the `KeyError` exception is raised. ## One can check with `hasKey proc<#hasKey,OrderedTableRef[A,B],A>`_ whether ## the key exists. ## @@ -1819,7 +1898,7 @@ proc `[]`*[A, B](t: OrderedTableRef[A, B], key: A): var B = ## a default value (e.g. zero for int) if the key doesn't exist ## * `getOrDefault proc<#getOrDefault,OrderedTableRef[A,B],A,B>`_ to return ## a custom value if the key doesn't exist - ## * `[]= proc<#[]=,OrderedTableRef[A,B],A,B>`_ for inserting a new + ## * `[]= proc<#[]=,OrderedTableRef[A,B],A,sinkB>`_ for inserting a new ## (key, value) pair in the table ## * `hasKey proc<#hasKey,OrderedTableRef[A,B],A>`_ for checking if ## a key is in the table @@ -1830,8 +1909,8 @@ proc `[]`*[A, B](t: OrderedTableRef[A, B], key: A): var B = echo a['z'] result = t[][key] -proc `[]=`*[A, B](t: OrderedTableRef[A, B], key: A, val: B) = - ## Inserts a ``(key, value)`` pair into ``t``. +proc `[]=`*[A, B](t: OrderedTableRef[A, B], key: A, val: sink B) = + ## Inserts a `(key, value)` pair into `t`. ## ## See also: ## * `[] proc<#[],OrderedTableRef[A,B],A>`_ for retrieving a value of a key @@ -1847,7 +1926,7 @@ proc `[]=`*[A, B](t: OrderedTableRef[A, B], key: A, val: B) = t[][key] = val proc hasKey*[A, B](t: OrderedTableRef[A, B], key: A): bool = - ## Returns true if ``key`` is in the table ``t``. + ## Returns true if `key` is in the table `t`. ## ## See also: ## * `contains proc<#contains,OrderedTableRef[A,B],A>`_ for use with the `in` @@ -1866,7 +1945,7 @@ proc hasKey*[A, B](t: OrderedTableRef[A, B], key: A): bool = proc contains*[A, B](t: OrderedTableRef[A, B], key: A): bool = ## Alias of `hasKey proc<#hasKey,OrderedTableRef[A,B],A>`_ for use with - ## the ``in`` operator. + ## the `in` operator. runnableExamples: let a = {'a': 5, 'b': 9}.newOrderedTable doAssert 'b' in a == true @@ -1874,8 +1953,8 @@ proc contains*[A, B](t: OrderedTableRef[A, B], key: A): bool = return hasKey[A, B](t, key) -proc hasKeyOrPut*[A, B](t: var OrderedTableRef[A, B], key: A, val: B): bool = - ## Returns true if ``key`` is in the table, otherwise inserts ``value``. +proc hasKeyOrPut*[A, B](t: OrderedTableRef[A, B], key: A, val: B): bool = + ## Returns true if `key` is in the table, otherwise inserts `value`. ## ## See also: ## * `hasKey proc<#hasKey,OrderedTableRef[A,B],A>`_ @@ -1895,8 +1974,8 @@ proc hasKeyOrPut*[A, B](t: var OrderedTableRef[A, B], key: A, val: B): bool = result = t[].hasKeyOrPut(key, val) proc getOrDefault*[A, B](t: OrderedTableRef[A, B], key: A): B = - ## Retrieves the value at ``t[key]`` if ``key`` is in ``t``. Otherwise, the - ## default initialization value for type ``B`` is returned (e.g. 0 for any + ## Retrieves the value at `t[key]` if `key` is in `t`. Otherwise, the + ## default initialization value for type `B` is returned (e.g. 0 for any ## integer type). ## ## See also: @@ -1914,8 +1993,8 @@ proc getOrDefault*[A, B](t: OrderedTableRef[A, B], key: A): B = getOrDefault(t[], key) proc getOrDefault*[A, B](t: OrderedTableRef[A, B], key: A, default: B): B = - ## Retrieves the value at ``t[key]`` if ``key`` is in ``t``. - ## Otherwise, ``default`` is returned. + ## Retrieves the value at `t[key]` if `key` is in `t`. + ## Otherwise, `default` is returned. ## ## See also: ## * `[] proc<#[],OrderedTableRef[A,B],A>`_ for retrieving a value of a key @@ -1932,7 +2011,7 @@ proc getOrDefault*[A, B](t: OrderedTableRef[A, B], key: A, default: B): B = getOrDefault(t[], key, default) proc mgetOrPut*[A, B](t: OrderedTableRef[A, B], key: A, val: B): var B = - ## Retrieves value at ``t[key]`` or puts ``val`` if not present, either way + ## Retrieves value at `t[key]` or puts `val` if not present, either way ## returning a value which can be modified. ## ## See also: @@ -1951,25 +2030,38 @@ proc mgetOrPut*[A, B](t: OrderedTableRef[A, B], key: A, val: B): var B = result = t[].mgetOrPut(key, val) +proc mgetOrPut*[A, B](t: OrderedTableRef[A, B], key: A): var B = + ## Retrieves the value at `t[key]` or puts the + ## default initialization value for type `B` (e.g. 0 for any + ## integer type). + runnableExamples: + var a = {'a': 5}.toOrderedTable + doAssert a.mgetOrPut('a') == 5 + a.mgetOrPut('z').inc + doAssert a == {'a': 5, 'z': 1}.toOrderedTable + + t[].mgetOrPut(key) + proc len*[A, B](t: OrderedTableRef[A, B]): int {.inline.} = - ## Returns the number of keys in ``t``. + ## Returns the number of keys in `t`. runnableExamples: let a = {'a': 5, 'b': 9}.newOrderedTable doAssert len(a) == 2 result = t.counter -proc add*[A, B](t: OrderedTableRef[A, B], key: A, val: B) = - ## Puts a new ``(key, value)`` pair into ``t`` even if ``t[key]`` already exists. +proc add*[A, B](t: OrderedTableRef[A, B], key: A, val: sink B) {.deprecated: + "Deprecated since v1.4; it was more confusing than useful, use `[]=`".} = + ## Puts a new `(key, value)` pair into `t` even if `t[key]` already exists. ## ## **This can introduce duplicate keys into the table!** ## - ## Use `[]= proc<#[]=,OrderedTableRef[A,B],A,B>`_ for inserting a new + ## Use `[]= proc<#[]=,OrderedTableRef[A,B],A,sinkB>`_ for inserting a new ## (key, value) pair in the table without introducing duplicates. t[].add(key, val) proc del*[A, B](t: OrderedTableRef[A, B], key: A) = - ## Deletes ``key`` from hash table ``t``. Does nothing if the key does not exist. + ## Deletes `key` from hash table `t`. Does nothing if the key does not exist. ## ## See also: ## * `clear proc<#clear,OrderedTableRef[A,B]>`_ to empty the whole table @@ -1983,9 +2075,9 @@ proc del*[A, B](t: OrderedTableRef[A, B], key: A) = t[].del(key) proc pop*[A, B](t: OrderedTableRef[A, B], key: A, val: var B): bool {.since: (1, 1).} = - ## Deletes the ``key`` from the table. - ## Returns ``true``, if the ``key`` existed, and sets ``val`` to the - ## mapping of the key. Otherwise, returns ``false``, and the ``val`` is + ## Deletes the `key` from the table. + ## Returns `true`, if the `key` existed, and sets `val` to the + ## mapping of the key. Otherwise, returns `false`, and the `val` is ## unchanged. ## ## See also: @@ -2019,15 +2111,15 @@ proc clear*[A, B](t: OrderedTableRef[A, B]) = clear(t[]) proc sort*[A, B](t: OrderedTableRef[A, B], cmp: proc (x, y: (A, B)): int, - order = SortOrder.Ascending) = - ## Sorts ``t`` according to the function ``cmp``. + order = SortOrder.Ascending) {.effectsOf: cmp.} = + ## Sorts `t` according to the function `cmp`. ## ## This modifies the internal list ## that kept the insertion order, so insertion order is lost after this - ## call but key lookup and insertions remain possible after ``sort`` (in + ## call but key lookup and insertions remain possible after `sort` (in ## contrast to the `sort proc<#sort,CountTableRef[A]>`_ for count tables). runnableExamples: - import algorithm + import std/[algorithm] var a = newOrderedTable[char, int]() for i, c in "cab": a[c] = 10*i @@ -2040,13 +2132,13 @@ proc sort*[A, B](t: OrderedTableRef[A, B], cmp: proc (x, y: (A, B)): int, t[].sort(cmp, order = order) proc `$`*[A, B](t: OrderedTableRef[A, B]): string = - ## The ``$`` operator for hash tables. Used internally when calling `echo` + ## The `$` operator for hash tables. Used internally when calling `echo` ## on a table. dollarImpl() proc `==`*[A, B](s, t: OrderedTableRef[A, B]): bool = - ## The ``==`` operator for ordered hash tables. Returns true if either both - ## tables are ``nil``, or neither is ``nil`` and the content and the order of + ## The `==` operator for ordered hash tables. Returns true if either both + ## tables are `nil`, or neither is `nil` and the content and the order of ## both are equal. runnableExamples: let @@ -2061,7 +2153,7 @@ proc `==`*[A, B](s, t: OrderedTableRef[A, B]): bool = iterator pairs*[A, B](t: OrderedTableRef[A, B]): (A, B) = - ## Iterates over any ``(key, value)`` pair in the table ``t`` in insertion + ## Iterates over any `(key, value)` pair in the table `t` in insertion ## order. ## ## See also: @@ -2071,7 +2163,7 @@ iterator pairs*[A, B](t: OrderedTableRef[A, B]): (A, B) = ## ## **Examples:** ## - ## .. code-block:: + ## ```Nim ## let a = { ## 'o': [1, 5, 7, 9], ## 'e': [2, 4, 6, 8] @@ -2085,6 +2177,7 @@ iterator pairs*[A, B](t: OrderedTableRef[A, B]): (A, B) = ## # value: [1, 5, 7, 9] ## # key: e ## # value: [2, 4, 6, 8] + ## ``` let L = len(t) forAllOrderedPairs: @@ -2092,7 +2185,7 @@ iterator pairs*[A, B](t: OrderedTableRef[A, B]): (A, B) = assert(len(t) == L, "the length of the table changed while iterating over it") iterator mpairs*[A, B](t: OrderedTableRef[A, B]): (A, var B) = - ## Iterates over any ``(key, value)`` pair in the table ``t`` in insertion + ## Iterates over any `(key, value)` pair in the table `t` in insertion ## order. The values can be modified. ## ## See also: @@ -2113,8 +2206,8 @@ iterator mpairs*[A, B](t: OrderedTableRef[A, B]): (A, var B) = yield (t.data[h].key, t.data[h].val) assert(len(t) == L, "the length of the table changed while iterating over it") -iterator keys*[A, B](t: OrderedTableRef[A, B]): A = - ## Iterates over any key in the table ``t`` in insertion order. +iterator keys*[A, B](t: OrderedTableRef[A, B]): lent A = + ## Iterates over any key in the table `t` in insertion order. ## ## See also: ## * `pairs iterator<#pairs.i,OrderedTableRef[A,B]>`_ @@ -2134,8 +2227,8 @@ iterator keys*[A, B](t: OrderedTableRef[A, B]): A = yield t.data[h].key assert(len(t) == L, "the length of the table changed while iterating over it") -iterator values*[A, B](t: OrderedTableRef[A, B]): B = - ## Iterates over any value in the table ``t`` in insertion order. +iterator values*[A, B](t: OrderedTableRef[A, B]): lent B = + ## Iterates over any value in the table `t` in insertion order. ## ## See also: ## * `pairs iterator<#pairs.i,OrderedTableRef[A,B]>`_ @@ -2155,7 +2248,7 @@ iterator values*[A, B](t: OrderedTableRef[A, B]): B = assert(len(t) == L, "the length of the table changed while iterating over it") iterator mvalues*[A, B](t: OrderedTableRef[A, B]): var B = - ## Iterates over any value in the table ``t`` in insertion order. The values + ## Iterates over any value in the table `t` in insertion order. The values ## can be modified. ## ## See also: @@ -2191,7 +2284,7 @@ type ## Hash table that counts the number of each key. ## ## For creating an empty CountTable, use `initCountTable proc - ## <#initCountTable,int>`_. + ## <#initCountTable>`_. data: seq[tuple[key: A, val: int]] counter: int isSorted: bool @@ -2199,7 +2292,7 @@ type ## `CountTable<#CountTable>`_. ## ## For creating a new empty CountTableRef, use `newCountTable proc - ## <#newCountTable,int>`_. + ## <#newCountTable>`_. # ------------------------------ helpers --------------------------------- @@ -2218,19 +2311,6 @@ proc enlarge[A](t: var CountTable[A]) = if t.data[i].val != 0: ctRawInsert(t, n, move t.data[i].key, move t.data[i].val) swap(t.data, n) -proc remove[A](t: var CountTable[A], key: A) = - var n: seq[tuple[key: A, val: int]] - newSeq(n, len(t.data)) - var removed: bool - for i in countup(0, high(t.data)): - if t.data[i].val != 0: - if t.data[i].key != key: - ctRawInsert(t, n, move t.data[i].key, move t.data[i].val) - else: - removed = true - swap(t.data, n) - if removed: dec(t.counter) - proc rawGet[A](t: CountTable[A], key: A): int = if t.data.len == 0: return -1 @@ -2244,42 +2324,36 @@ template ctget(t, key, default: untyped): untyped = var index = rawGet(t, key) result = if index >= 0: t.data[index].val else: default -proc inc*[A](t: var CountTable[A], key: A, val: Positive = 1) +proc inc*[A](t: var CountTable[A], key: A, val = 1) # ---------------------------------------------------------------------- proc initCountTable*[A](initialSize = defaultInitialSize): CountTable[A] = ## Creates a new count table that is empty. ## - ## ``initialSize`` must be a power of two (default: 64). - ## If you need to accept runtime values for this you could use the - ## `nextPowerOfTwo proc<math.html#nextPowerOfTwo,int>`_ from the - ## `math module<math.html>`_ or the `rightSize proc<#rightSize,Natural>`_ - ## from this module. - ## ## Starting from Nim v0.20, tables are initialized by default and it is ## not necessary to call this function explicitly. ## ## See also: ## * `toCountTable proc<#toCountTable,openArray[A]>`_ - ## * `newCountTable proc<#newCountTable,int>`_ for creating a + ## * `newCountTable proc<#newCountTable>`_ for creating a ## `CountTableRef` + result = default(CountTable[A]) initImpl(result, initialSize) proc toCountTable*[A](keys: openArray[A]): CountTable[A] = - ## Creates a new count table with every member of a container ``keys`` + ## Creates a new count table with every member of a container `keys` ## having a count of how many times it occurs in that container. - result = initCountTable[A](rightSize(keys.len)) + result = initCountTable[A](keys.len) for key in items(keys): result.inc(key) proc `[]`*[A](t: CountTable[A], key: A): int = - ## Retrieves the value at ``t[key]`` if ``key`` is in ``t``. - ## Otherwise ``0`` is returned. + ## Retrieves the value at `t[key]` if `key` is in `t`. + ## Otherwise `0` is returned. ## ## See also: ## * `getOrDefault<#getOrDefault,CountTable[A],A,int>`_ to return ## a custom value if the key doesn't exist - ## * `mget proc<#mget,CountTable[A],A>`_ ## * `[]= proc<#[]%3D,CountTable[A],A,int>`_ for inserting a new ## (key, value) pair in the table ## * `hasKey proc<#hasKey,CountTable[A],A>`_ for checking if a key @@ -2287,17 +2361,21 @@ proc `[]`*[A](t: CountTable[A], key: A): int = assert(not t.isSorted, "CountTable must not be used after sorting") ctget(t, key, 0) +template cntMakeEmpty(i) = t.data[i].val = 0 +template cntCellEmpty(i) = t.data[i].val == 0 +template cntCellHash(i) = hash(t.data[i].key) + proc `[]=`*[A](t: var CountTable[A], key: A, val: int) = - ## Inserts a ``(key, value)`` pair into ``t``. + ## Inserts a `(key, value)` pair into `t`. ## ## See also: ## * `[] proc<#[],CountTable[A],A>`_ for retrieving a value of a key - ## * `inc proc<#inc,CountTable[A],A,Positive>`_ for incrementing a + ## * `inc proc<#inc,CountTable[A],A,int>`_ for incrementing a ## value of a key assert(not t.isSorted, "CountTable must not be used after sorting") assert val >= 0 if val == 0: - t.remove(key) + delImplNoHCode(cntMakeEmpty, cntCellEmpty, cntCellHash) else: let h = rawGet(t, key) if h >= 0: @@ -2305,11 +2383,8 @@ proc `[]=`*[A](t: var CountTable[A], key: A, val: int) = else: insertImpl() -proc inc*[A](t: var CountTable[A], key: A, val: Positive = 1) = - ## Increments ``t[key]`` by ``val`` (default: 1). - ## - ## ``val`` must be a positive number. If you need to decrement a value, - ## use a regular ``Table`` instead. +proc inc*[A](t: var CountTable[A], key: A, val = 1) = + ## Increments `t[key]` by `val` (default: 1). runnableExamples: var a = toCountTable("aab") a.inc('a') @@ -2320,16 +2395,22 @@ proc inc*[A](t: var CountTable[A], key: A, val: Positive = 1) = var index = rawGet(t, key) if index >= 0: inc(t.data[index].val, val) - if t.data[index].val == 0: dec(t.counter) + if t.data[index].val == 0: + delImplIdx(t, index, cntMakeEmpty, cntCellEmpty, cntCellHash) else: - insertImpl() + if val != 0: + insertImpl() + +proc len*[A](t: CountTable[A]): int = + ## Returns the number of keys in `t`. + result = t.counter proc smallest*[A](t: CountTable[A]): tuple[key: A, val: int] = - ## Returns the ``(key, value)`` pair with the smallest ``val``. Efficiency: O(n) + ## Returns the `(key, value)` pair with the smallest `val`. Efficiency: O(n) ## ## See also: ## * `largest proc<#largest,CountTable[A]>`_ - assert t.len > 0 + assert t.len > 0, "counttable is empty" var minIdx = -1 for h in 0 .. high(t.data): if t.data[h].val > 0 and (minIdx == -1 or t.data[minIdx].val > t.data[h].val): @@ -2338,11 +2419,11 @@ proc smallest*[A](t: CountTable[A]): tuple[key: A, val: int] = result.val = t.data[minIdx].val proc largest*[A](t: CountTable[A]): tuple[key: A, val: int] = - ## Returns the ``(key, value)`` pair with the largest ``val``. Efficiency: O(n) + ## Returns the `(key, value)` pair with the largest `val`. Efficiency: O(n) ## ## See also: ## * `smallest proc<#smallest,CountTable[A]>`_ - assert t.len > 0 + assert t.len > 0, "counttable is empty" var maxIdx = 0 for h in 1 .. high(t.data): if t.data[maxIdx].val < t.data[h].val: maxIdx = h @@ -2350,7 +2431,7 @@ proc largest*[A](t: CountTable[A]): tuple[key: A, val: int] = result.val = t.data[maxIdx].val proc hasKey*[A](t: CountTable[A], key: A): bool = - ## Returns true if ``key`` is in the table ``t``. + ## Returns true if `key` is in the table `t`. ## ## See also: ## * `contains proc<#contains,CountTable[A],A>`_ for use with the `in` @@ -2363,12 +2444,12 @@ proc hasKey*[A](t: CountTable[A], key: A): bool = proc contains*[A](t: CountTable[A], key: A): bool = ## Alias of `hasKey proc<#hasKey,CountTable[A],A>`_ for use with - ## the ``in`` operator. + ## the `in` operator. return hasKey[A](t, key) proc getOrDefault*[A](t: CountTable[A], key: A; default: int = 0): int = - ## Retrieves the value at ``t[key]`` if``key`` is in ``t``. Otherwise, the - ## integer value of ``default`` is returned. + ## Retrieves the value at `t[key]` if `key` is in `t`. Otherwise, the + ## integer value of `default` is returned. ## ## See also: ## * `[] proc<#[],CountTable[A],A>`_ for retrieving a value of a key @@ -2376,14 +2457,8 @@ proc getOrDefault*[A](t: CountTable[A], key: A; default: int = 0): int = ## is in the table ctget(t, key, default) -proc len*[A](t: CountTable[A]): int = - ## Returns the number of keys in ``t``. - result = t.counter - proc del*[A](t: var CountTable[A], key: A) {.since: (1, 1).} = - ## Deletes ``key`` from table ``t``. Does nothing if the key does not exist. - ## - ## O(n) complexity. + ## Deletes `key` from table `t`. Does nothing if the key does not exist. ## ## See also: ## * `pop proc<#pop,CountTable[A],A,int>`_ @@ -2397,16 +2472,14 @@ proc del*[A](t: var CountTable[A], key: A) {.since: (1, 1).} = a.del('c') assert a == toCountTable("aa") - remove(t, key) + delImplNoHCode(cntMakeEmpty, cntCellEmpty, cntCellHash) proc pop*[A](t: var CountTable[A], key: A, val: var int): bool {.since: (1, 1).} = - ## Deletes the ``key`` from the table. - ## Returns ``true``, if the ``key`` existed, and sets ``val`` to the - ## mapping of the key. Otherwise, returns ``false``, and the ``val`` is + ## Deletes the `key` from the table. + ## Returns `true`, if the `key` existed, and sets `val` to the + ## mapping of the key. Otherwise, returns `false`, and the `val` is ## unchanged. ## - ## O(n) complexity. - ## ## See also: ## * `del proc<#del,CountTable[A],A>`_ ## * `clear proc<#clear,CountTable[A]>`_ to empty the whole table @@ -2423,7 +2496,7 @@ proc pop*[A](t: var CountTable[A], key: A, val: var int): bool {.since: (1, 1).} result = index >= 0 if result: val = move(t.data[index].val) - remove(t, key) + delImplIdx(t, index, cntMakeEmpty, cntCellEmpty, cntCellHash) proc clear*[A](t: var CountTable[A]) = ## Resets the table so that it is empty. @@ -2441,13 +2514,13 @@ proc sort*[A](t: var CountTable[A], order = SortOrder.Descending) = ## Sorts the count table so that, by default, the entry with the ## highest counter comes first. ## - ## **WARNING:** This is destructive! Once sorted, you must not modify ``t`` afterwards! + ## .. warning:: This is destructive! Once sorted, you must not modify `t` afterwards! ## ## You can use the iterators `pairs<#pairs.i,CountTable[A]>`_, ## `keys<#keys.i,CountTable[A]>`_, and `values<#values.i,CountTable[A]>`_ - ## to iterate over ``t`` in the sorted order. + ## to iterate over `t` in the sorted order. runnableExamples: - import algorithm, sequtils + import std/[algorithm, sequtils] var a = toCountTable("abracadabra") doAssert a == "aaaaabbrrcd".toCountTable a.sort() @@ -2485,18 +2558,18 @@ when (NimMajor, NimMinor) <= (1, 0): result.inc(key, value) proc `$`*[A](t: CountTable[A]): string = - ## The ``$`` operator for count tables. Used internally when calling `echo` + ## The `$` operator for count tables. Used internally when calling `echo` ## on a table. dollarImpl() proc `==`*[A](s, t: CountTable[A]): bool = - ## The ``==`` operator for count tables. Returns ``true`` if both tables + ## The `==` operator for count tables. Returns `true` if both tables ## contain the same keys with the same count. Insert order does not matter. equalsImpl(s, t) iterator pairs*[A](t: CountTable[A]): (A, int) = - ## Iterates over any ``(key, value)`` pair in the table ``t``. + ## Iterates over any `(key, value)` pair in the table `t`. ## ## See also: ## * `mpairs iterator<#mpairs.i,CountTable[A]>`_ @@ -2505,7 +2578,7 @@ iterator pairs*[A](t: CountTable[A]): (A, int) = ## ## **Examples:** ## - ## .. code-block:: + ## ```Nim ## let a = toCountTable("abracadabra") ## ## for k, v in pairs(a): @@ -2522,6 +2595,7 @@ iterator pairs*[A](t: CountTable[A]): (A, int) = ## # value: 1 ## # key: r ## # value: 2 + ## ``` let L = len(t) for h in 0 .. high(t.data): if t.data[h].val != 0: @@ -2529,7 +2603,7 @@ iterator pairs*[A](t: CountTable[A]): (A, int) = assert(len(t) == L, "the length of the table changed while iterating over it") iterator mpairs*[A](t: var CountTable[A]): (A, var int) = - ## Iterates over any ``(key, value)`` pair in the table ``t`` (must be + ## Iterates over any `(key, value)` pair in the table `t` (must be ## declared as `var`). The values can be modified. ## ## See also: @@ -2547,8 +2621,8 @@ iterator mpairs*[A](t: var CountTable[A]): (A, var int) = yield (t.data[h].key, t.data[h].val) assert(len(t) == L, "the length of the table changed while iterating over it") -iterator keys*[A](t: CountTable[A]): A = - ## Iterates over any key in the table ``t``. +iterator keys*[A](t: CountTable[A]): lent A = + ## Iterates over any key in the table `t`. ## ## See also: ## * `pairs iterator<#pairs.i,CountTable[A]>`_ @@ -2566,7 +2640,7 @@ iterator keys*[A](t: CountTable[A]): A = assert(len(t) == L, "the length of the table changed while iterating over it") iterator values*[A](t: CountTable[A]): int = - ## Iterates over any value in the table ``t``. + ## Iterates over any value in the table `t`. ## ## See also: ## * `pairs iterator<#pairs.i,CountTable[A]>`_ @@ -2584,7 +2658,7 @@ iterator values*[A](t: CountTable[A]): int = assert(len(t) == L, "the length of the table changed while iterating over it") iterator mvalues*[A](t: var CountTable[A]): var int = - ## Iterates over any value in the table ``t`` (must be + ## Iterates over any value in the table `t` (must be ## declared as `var`). The values can be modified. ## ## See also: @@ -2614,37 +2688,33 @@ iterator mvalues*[A](t: var CountTable[A]): var int = proc inc*[A](t: CountTableRef[A], key: A, val = 1) -proc newCountTable*[A](initialSize = defaultInitialSize): <//>CountTableRef[A] = +proc newCountTable*[A](initialSize = defaultInitialSize): CountTableRef[A] = ## Creates a new ref count table that is empty. ## - ## ``initialSize`` must be a power of two (default: 64). - ## If you need to accept runtime values for this you could use the - ## `nextPowerOfTwo proc<math.html#nextPowerOfTwo,int>`_ from the - ## `math module<math.html>`_ or the `rightSize proc<#rightSize,Natural>`_ - ## from this module. - ## ## See also: ## * `newCountTable proc<#newCountTable,openArray[A]>`_ for creating ## a `CountTableRef` from a collection - ## * `initCountTable proc<#initCountTable,int>`_ for creating a + ## * `initCountTable proc<#initCountTable>`_ for creating a ## `CountTable` new(result) - result[] = initCountTable[A](initialSize) + {.noSideEffect.}: + result[] = initCountTable[A](initialSize) -proc newCountTable*[A](keys: openArray[A]): <//>CountTableRef[A] = - ## Creates a new ref count table with every member of a container ``keys`` +proc newCountTable*[A](keys: openArray[A]): CountTableRef[A] = + ## Creates a new ref count table with every member of a container `keys` ## having a count of how many times it occurs in that container. - result = newCountTable[A](rightSize(keys.len)) - for key in items(keys): result.inc(key) + result = newCountTable[A](keys.len) + {.noSideEffect.}: + for key in items(keys): result.inc(key) proc `[]`*[A](t: CountTableRef[A], key: A): int = - ## Retrieves the value at ``t[key]`` if ``key`` is in ``t``. - ## Otherwise ``0`` is returned. + ## Retrieves the value at `t[key]` if `key` is in `t`. + ## Otherwise `0` is returned. ## ## See also: ## * `getOrDefault<#getOrDefault,CountTableRef[A],A,int>`_ to return ## a custom value if the key doesn't exist - ## * `mget proc<#mget,CountTableRef[A],A>`_ + ## * `inc proc<#inc,CountTableRef[A],A,int>`_ to inc even if missing ## * `[]= proc<#[]%3D,CountTableRef[A],A,int>`_ for inserting a new ## (key, value) pair in the table ## * `hasKey proc<#hasKey,CountTableRef[A],A>`_ for checking if a key @@ -2652,40 +2722,42 @@ proc `[]`*[A](t: CountTableRef[A], key: A): int = result = t[][key] proc `[]=`*[A](t: CountTableRef[A], key: A, val: int) = - ## Inserts a ``(key, value)`` pair into ``t``. + ## Inserts a `(key, value)` pair into `t`. ## ## See also: ## * `[] proc<#[],CountTableRef[A],A>`_ for retrieving a value of a key ## * `inc proc<#inc,CountTableRef[A],A,int>`_ for incrementing a ## value of a key assert val > 0 - t[][key] = val + {.noSideEffect.}: + t[][key] = val proc inc*[A](t: CountTableRef[A], key: A, val = 1) = - ## Increments ``t[key]`` by ``val`` (default: 1). + ## Increments `t[key]` by `val` (default: 1). runnableExamples: var a = newCountTable("aab") a.inc('a') a.inc('b', 10) doAssert a == newCountTable("aaabbbbbbbbbbb") - t[].inc(key, val) + {.noSideEffect.}: + t[].inc(key, val) proc smallest*[A](t: CountTableRef[A]): tuple[key: A, val: int] = - ## Returns the ``(key, value)`` pair with the smallest ``val``. Efficiency: O(n) + ## Returns the `(key, value)` pair with the smallest `val`. Efficiency: O(n) ## ## See also: ## * `largest proc<#largest,CountTableRef[A]>`_ t[].smallest proc largest*[A](t: CountTableRef[A]): tuple[key: A, val: int] = - ## Returns the ``(key, value)`` pair with the largest ``val``. Efficiency: O(n) + ## Returns the `(key, value)` pair with the largest `val`. Efficiency: O(n) ## ## See also: ## * `smallest proc<#smallest,CountTable[A]>`_ t[].largest proc hasKey*[A](t: CountTableRef[A], key: A): bool = - ## Returns true if ``key`` is in the table ``t``. + ## Returns true if `key` is in the table `t`. ## ## See also: ## * `contains proc<#contains,CountTableRef[A],A>`_ for use with the `in` @@ -2697,12 +2769,12 @@ proc hasKey*[A](t: CountTableRef[A], key: A): bool = proc contains*[A](t: CountTableRef[A], key: A): bool = ## Alias of `hasKey proc<#hasKey,CountTableRef[A],A>`_ for use with - ## the ``in`` operator. + ## the `in` operator. return hasKey[A](t, key) proc getOrDefault*[A](t: CountTableRef[A], key: A, default: int): int = - ## Retrieves the value at ``t[key]`` if``key`` is in ``t``. Otherwise, the - ## integer value of ``default`` is returned. + ## Retrieves the value at `t[key]` if `key` is in `t`. Otherwise, the + ## integer value of `default` is returned. ## ## See also: ## * `[] proc<#[],CountTableRef[A],A>`_ for retrieving a value of a key @@ -2711,13 +2783,11 @@ proc getOrDefault*[A](t: CountTableRef[A], key: A, default: int): int = result = t[].getOrDefault(key, default) proc len*[A](t: CountTableRef[A]): int = - ## Returns the number of keys in ``t``. + ## Returns the number of keys in `t`. result = t.counter proc del*[A](t: CountTableRef[A], key: A) {.since: (1, 1).} = - ## Deletes ``key`` from table ``t``. Does nothing if the key does not exist. - ## - ## O(n) complexity. + ## Deletes `key` from table `t`. Does nothing if the key does not exist. ## ## See also: ## * `pop proc<#pop,CountTableRef[A],A,int>`_ @@ -2725,13 +2795,11 @@ proc del*[A](t: CountTableRef[A], key: A) {.since: (1, 1).} = del(t[], key) proc pop*[A](t: CountTableRef[A], key: A, val: var int): bool {.since: (1, 1).} = - ## Deletes the ``key`` from the table. - ## Returns ``true``, if the ``key`` existed, and sets ``val`` to the - ## mapping of the key. Otherwise, returns ``false``, and the ``val`` is + ## Deletes the `key` from the table. + ## Returns `true`, if the `key` existed, and sets `val` to the + ## mapping of the key. Otherwise, returns `false`, and the `val` is ## unchanged. ## - ## O(n) complexity. - ## ## See also: ## * `del proc<#del,CountTableRef[A],A>`_ ## * `clear proc<#clear,CountTableRef[A]>`_ to empty the whole table @@ -2753,7 +2821,7 @@ proc sort*[A](t: CountTableRef[A], order = SortOrder.Descending) = ## ## You can use the iterators `pairs<#pairs.i,CountTableRef[A]>`_, ## `keys<#keys.i,CountTableRef[A]>`_, and `values<#values.i,CountTableRef[A]>`_ - ## to iterate over ``t`` in the sorted order. + ## to iterate over `t` in the sorted order. t[].sort(order = order) proc merge*[A](s, t: CountTableRef[A]) = @@ -2768,13 +2836,13 @@ proc merge*[A](s, t: CountTableRef[A]) = s[].merge(t[]) proc `$`*[A](t: CountTableRef[A]): string = - ## The ``$`` operator for count tables. Used internally when calling `echo` + ## The `$` operator for count tables. Used internally when calling `echo` ## on a table. dollarImpl() proc `==`*[A](s, t: CountTableRef[A]): bool = - ## The ``==`` operator for count tables. Returns ``true`` if either both tables - ## are ``nil``, or neither is ``nil`` and both contain the same keys with the same + ## The `==` operator for count tables. Returns `true` if either both tables + ## are `nil`, or neither is `nil` and both contain the same keys with the same ## count. Insert order does not matter. if isNil(s): result = isNil(t) elif isNil(t): result = false @@ -2782,7 +2850,7 @@ proc `==`*[A](s, t: CountTableRef[A]): bool = iterator pairs*[A](t: CountTableRef[A]): (A, int) = - ## Iterates over any ``(key, value)`` pair in the table ``t``. + ## Iterates over any `(key, value)` pair in the table `t`. ## ## See also: ## * `mpairs iterator<#mpairs.i,CountTableRef[A]>`_ @@ -2791,7 +2859,7 @@ iterator pairs*[A](t: CountTableRef[A]): (A, int) = ## ## **Examples:** ## - ## .. code-block:: + ## ```Nim ## let a = newCountTable("abracadabra") ## ## for k, v in pairs(a): @@ -2808,6 +2876,7 @@ iterator pairs*[A](t: CountTableRef[A]): (A, int) = ## # value: 1 ## # key: r ## # value: 2 + ## ``` let L = len(t) for h in 0 .. high(t.data): if t.data[h].val != 0: @@ -2815,7 +2884,7 @@ iterator pairs*[A](t: CountTableRef[A]): (A, int) = assert(len(t) == L, "the length of the table changed while iterating over it") iterator mpairs*[A](t: CountTableRef[A]): (A, var int) = - ## Iterates over any ``(key, value)`` pair in the table ``t``. The values can + ## Iterates over any `(key, value)` pair in the table `t`. The values can ## be modified. ## ## See also: @@ -2834,7 +2903,7 @@ iterator mpairs*[A](t: CountTableRef[A]): (A, var int) = assert(len(t) == L, "table modified while iterating over it") iterator keys*[A](t: CountTableRef[A]): A = - ## Iterates over any key in the table ``t``. + ## Iterates over any key in the table `t`. ## ## See also: ## * `pairs iterator<#pairs.i,CountTable[A]>`_ @@ -2852,7 +2921,7 @@ iterator keys*[A](t: CountTableRef[A]): A = assert(len(t) == L, "the length of the table changed while iterating over it") iterator values*[A](t: CountTableRef[A]): int = - ## Iterates over any value in the table ``t``. + ## Iterates over any value in the table `t`. ## ## See also: ## * `pairs iterator<#pairs.i,CountTableRef[A]>`_ @@ -2870,7 +2939,7 @@ iterator values*[A](t: CountTableRef[A]): int = assert(len(t) == L, "the length of the table changed while iterating over it") iterator mvalues*[A](t: CountTableRef[A]): var int = - ## Iterates over any value in the table ``t``. The values can be modified. + ## Iterates over any value in the table `t`. The values can be modified. ## ## See also: ## * `mpairs iterator<#mpairs.i,CountTableRef[A]>`_ @@ -2887,316 +2956,17 @@ iterator mvalues*[A](t: CountTableRef[A]): var int = yield t.data[h].val assert(len(t) == L, "the length of the table changed while iterating over it") +proc hash*[K,V](s: Table[K,V]): Hash = + for p in pairs(s): + result = result xor hash(p) + result = !$result +proc hash*[K,V](s: OrderedTable[K,V]): Hash = + for p in pairs(s): + result = result !& hash(p) + result = !$result - -when isMainModule: - type - Person = object - firstName, lastName: string - - proc hash(x: Person): Hash = - ## Piggyback on the already available string hash proc. - ## - ## Without this proc nothing works! - result = x.firstName.hash !& x.lastName.hash - result = !$result - - var - salaries = initTable[Person, int]() - p1, p2: Person - p1.firstName = "Jon" - p1.lastName = "Ross" - salaries[p1] = 30_000 - p2.firstName = "소진" - p2.lastName = "박" - salaries[p2] = 45_000 - var - s2 = initOrderedTable[Person, int]() - s3 = initCountTable[Person]() - s2[p1] = 30_000 - s2[p2] = 45_000 - s3[p1] = 30_000 - s3[p2] = 45_000 - - block: # Ordered table should preserve order after deletion - var - s4 = initOrderedTable[int, int]() - s4[1] = 1 - s4[2] = 2 - s4[3] = 3 - - var prev = 0 - for i in s4.values: - doAssert(prev < i) - prev = i - - s4.del(2) - doAssert(2 notin s4) - doAssert(s4.len == 2) - prev = 0 - for i in s4.values: - doAssert(prev < i) - prev = i - - block: # Deletion from OrderedTable should account for collision groups. See issue #5057. - # The bug is reproducible only with exact keys - const key1 = "boy_jackpot.inGamma" - const key2 = "boy_jackpot.outBlack" - - var t = { - key1: 0, - key2: 0 - }.toOrderedTable() - - t.del(key1) - assert(t.len == 1) - assert(key2 in t) - - var - t1 = initCountTable[string]() - t2 = initCountTable[string]() - t1.inc("foo") - t1.inc("bar", 2) - t1.inc("baz", 3) - t2.inc("foo", 4) - t2.inc("bar") - t2.inc("baz", 11) - merge(t1, t2) - assert(t1["foo"] == 5) - assert(t1["bar"] == 3) - assert(t1["baz"] == 14) - - let - t1r = newCountTable[string]() - t2r = newCountTable[string]() - t1r.inc("foo") - t1r.inc("bar", 2) - t1r.inc("baz", 3) - t2r.inc("foo", 4) - t2r.inc("bar") - t2r.inc("baz", 11) - merge(t1r, t2r) - assert(t1r["foo"] == 5) - assert(t1r["bar"] == 3) - assert(t1r["baz"] == 14) - - var - t1l = initCountTable[string]() - t2l = initCountTable[string]() - t1l.inc("foo") - t1l.inc("bar", 2) - t1l.inc("baz", 3) - t2l.inc("foo", 4) - t2l.inc("bar") - t2l.inc("baz", 11) - - block: - const testKey = "TESTKEY" - let t: CountTableRef[string] = newCountTable[string]() - - # Before, does not compile with error message: - #test_counttable.nim(7, 43) template/generic instantiation from here - #lib/pure/collections/tables.nim(117, 21) template/generic instantiation from here - #lib/pure/collections/tableimpl.nim(32, 27) Error: undeclared field: 'hcode - doAssert 0 == t[testKey] - t.inc(testKey, 3) - doAssert 3 == t[testKey] - - block: - # Clear tests - var clearTable = newTable[int, string]() - clearTable[42] = "asd" - clearTable[123123] = "piuyqwb " - doAssert clearTable[42] == "asd" - clearTable.clear() - doAssert(not clearTable.hasKey(123123)) - doAssert clearTable.getOrDefault(42) == "" - - block: #5482 - var a = [("wrong?", "foo"), ("wrong?", "foo2")].newOrderedTable() - var b = newOrderedTable[string, string](initialSize = 2) - b.add("wrong?", "foo") - b.add("wrong?", "foo2") - assert a == b - - block: #5482 - var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable() - var b = newOrderedTable[string, string](initialSize = 2) - b.add("wrong?", "foo") - b.add("wrong?", "foo2") - assert a == b - - block: #5487 - var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable() - var b = newOrderedTable[string, string]() # notice, default size! - b.add("wrong?", "foo") - b.add("wrong?", "foo2") - assert a == b - - block: #5487 - var a = [("wrong?", "foo"), ("wrong?", "foo2")].newOrderedTable() - var b = newOrderedTable[string, string]() # notice, default size! - b.add("wrong?", "foo") - b.add("wrong?", "foo2") - assert a == b - - block: - var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable() - var b = [("wrong?", "foo"), ("wrong?", "foo2")].newOrderedTable() - var c = newOrderedTable[string, string]() # notice, default size! - c.add("wrong?", "foo") - c.add("wrong?", "foo2") - assert a == b - assert a == c - - block: #6250 - let - a = {3: 1}.toOrderedTable - b = {3: 2}.toOrderedTable - assert((a == b) == false) - assert((b == a) == false) - - block: #6250 - let - a = {3: 2}.toOrderedTable - b = {3: 2}.toOrderedTable - assert((a == b) == true) - assert((b == a) == true) - - block: # CountTable.smallest - let t = toCountTable([0, 0, 5, 5, 5]) - doAssert t.smallest == (0, 2) - - block: #10065 - let t = toCountTable("abracadabra") - doAssert t['z'] == 0 - - var t_mut = toCountTable("abracadabra") - doAssert t_mut['z'] == 0 - # the previous read may not have modified the table. - doAssert t_mut.hasKey('z') == false - t_mut['z'] = 1 - doAssert t_mut['z'] == 1 - doAssert t_mut.hasKey('z') == true - - block: #12813 #13079 - var t = toCountTable("abracadabra") - doAssert len(t) == 5 - - t['a'] = 0 # remove a key - doAssert len(t) == 4 - - block: - var tp: Table[string, string] = initTable[string, string]() - doAssert "test1" == tp.getOrDefault("test1", "test1") - tp["test2"] = "test2" - doAssert "test2" == tp.getOrDefault("test2", "test1") - var tr: TableRef[string, string] = newTable[string, string]() - doAssert "test1" == tr.getOrDefault("test1", "test1") - tr["test2"] = "test2" - doAssert "test2" == tr.getOrDefault("test2", "test1") - var op: OrderedTable[string, string] = initOrderedTable[string, string]() - doAssert "test1" == op.getOrDefault("test1", "test1") - op["test2"] = "test2" - doAssert "test2" == op.getOrDefault("test2", "test1") - var orf: OrderedTableRef[string, string] = newOrderedTable[string, string]() - doAssert "test1" == orf.getOrDefault("test1", "test1") - orf["test2"] = "test2" - doAssert "test2" == orf.getOrDefault("test2", "test1") - - block tableWithoutInit: - var - a: Table[string, int] - b: Table[string, int] - c: Table[string, int] - d: Table[string, int] - e: Table[string, int] - - a["a"] = 7 - doAssert a.hasKey("a") - doAssert a.len == 1 - doAssert a["a"] == 7 - a["a"] = 9 - doAssert a.len == 1 - doAssert a["a"] == 9 - - doAssert b.hasKeyOrPut("b", 5) == false - doAssert b.hasKey("b") - doAssert b.hasKeyOrPut("b", 8) - doAssert b["b"] == 5 - - doAssert c.getOrDefault("a") == 0 - doAssert c.getOrDefault("a", 3) == 3 - c["a"] = 6 - doAssert c.getOrDefault("a", 3) == 6 - - doAssert d.mgetOrPut("a", 3) == 3 - doAssert d.mgetOrPut("a", 6) == 3 - - var x = 99 - doAssert e.pop("a", x) == false - doAssert x == 99 - e["a"] = 77 - doAssert e.pop("a", x) - doAssert x == 77 - - block orderedTableWithoutInit: - var - a: OrderedTable[string, int] - b: OrderedTable[string, int] - c: OrderedTable[string, int] - d: OrderedTable[string, int] - - a["a"] = 7 - doAssert a.hasKey("a") - doAssert a.len == 1 - doAssert a["a"] == 7 - a["a"] = 9 - doAssert a.len == 1 - doAssert a["a"] == 9 - - doAssert b.hasKeyOrPut("b", 5) == false - doAssert b.hasKey("b") - doAssert b.hasKeyOrPut("b", 8) - doAssert b["b"] == 5 - - doAssert c.getOrDefault("a") == 0 - doAssert c.getOrDefault("a", 3) == 3 - c["a"] = 6 - doAssert c.getOrDefault("a", 3) == 6 - - doAssert d.mgetOrPut("a", 3) == 3 - doAssert d.mgetOrPut("a", 6) == 3 - - block countTableWithoutInit: - var - a: CountTable[string] - b: CountTable[string] - c: CountTable[string] - d: CountTable[string] - e: CountTable[string] - - a["a"] = 7 - doAssert a.hasKey("a") - doAssert a.len == 1 - doAssert a["a"] == 7 - a["a"] = 9 - doAssert a.len == 1 - doAssert a["a"] == 9 - - doAssert b["b"] == 0 - b.inc("b") - doAssert b["b"] == 1 - - doAssert c.getOrDefault("a") == 0 - doAssert c.getOrDefault("a", 3) == 3 - c["a"] = 6 - doAssert c.getOrDefault("a", 3) == 6 - - e["f"] = 3 - merge(d, e) - doAssert d.hasKey("f") - d.inc("f") - merge(d, e) - doAssert d["f"] == 7 +proc hash*[V](s: CountTable[V]): Hash = + for p in pairs(s): + result = result xor hash(p) + result = !$result |