diff options
Diffstat (limited to 'lib/pure/collections/sequtils.nim')
-rw-r--r-- | lib/pure/collections/sequtils.nim | 1100 |
1 files changed, 395 insertions, 705 deletions
diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index d56742ba9..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 @@ -73,22 +80,28 @@ ## heterogeneous members -include "system/inclrtl" +import std/private/since + +import std/macros +from std/typetraits import supportsCopyMem -import macros +when defined(nimPreviewSlimSystem): + import std/assertions -when not defined(nimhygiene): - {.pragma: dirty.} +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] @@ -281,19 +319,33 @@ when (NimMajor, NimMinor) <= (1, 0): else: zipImpl(s1, s2, seq[(S, T)]) -proc distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] = +proc unzip*[S, T](s: openArray[(S, T)]): (seq[S], seq[T]) {.since: (1, 1).} = + ## Returns a tuple of two sequences split out from a sequence of 2-field tuples. + runnableExamples: + let + zipped = @[(1, 'a'), (2, 'b'), (3, 'c')] + unzipped1 = @[1, 2, 3] + unzipped2 = @['a', 'b', 'c'] + assert zipped.unzip() == (unzipped1, unzipped2) + assert zip(unzipped1, unzipped2).unzip() == (unzipped1, unzipped2) + 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] + +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. @@ -308,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) @@ -339,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 ## @@ -361,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)>`_ ## @@ -381,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)>`_ ## @@ -400,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: @@ -420,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`). ## - ## 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>`_ ## * `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 @@ -443,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)>`_ ## @@ -472,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) @@ -486,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]) @@ -497,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] @@ -512,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. @@ -531,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')`. + ## + ## Instead of using `mapIt` and `filterIt`, 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)>`_ ## * `filter iterator<#filter.i,openArray[T],proc(T)>`_ ## runnableExamples: @@ -551,20 +666,20 @@ template filterIt*(s, pred: untyped): untyped = assert acceptable == @[-2.0, 24.5, 44.31] assert notAcceptable == @[-272.15, 99.9, -113.44] - var result = newSeq[type(s[0])]() + var result = newSeq[typeof(s[0])]() for it {.inject.} in items(s): if pred: result.add(it) 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>`_ ## @@ -585,23 +700,42 @@ template keepItIf*(varSeq: seq, pred: untyped) = inc(pos) setLen(varSeq, pos) -proc all*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): bool = +since (1, 1): + template countIt*(s, pred: untyped): int = + ## 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)`. + ## + runnableExamples: + let numbers = @[-3, -2, -1, 0, 1, 2, 3, 4, 5, 6] + iterator iota(n: int): int = + for i in 0..<n: yield i + assert numbers.countIt(it < 0) == 3 + assert countIt(iota(10), it < 2) == 2 + + var result = 0 + for it {.inject.} in s: + if pred: result += 1 + result + +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 @@ -609,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>`_ ## @@ -627,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>`_ ## @@ -671,7 +805,7 @@ template anyIt*(s, pred: untyped): bool = template toSeq1(s: not iterator): untyped = # overload for typed but not iterator - type OutType = type(items(s)) + type OutType = typeof(items(s)) when compiles(s.len): block: evalOnceAs(s2, s, compiles((let _ = s))) @@ -682,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 @@ -692,14 +826,14 @@ template toSeq2(iter: iterator): untyped = evalOnceAs(iter2, iter(), false) when compiles(iter2.len): var i = 0 - var result = newSeq[type(iter2)](iter2.len) + var result = newSeq[typeof(iter2)](iter2.len) for x in iter2: result[i] = x inc i result else: - type OutType = type(iter2()) - var result: seq[OutType] = @[] + type OutType = typeof(iter2()) + var result: seq[OutType]# = @[] when compiles(iter2()): evalOnceAs(iter4, iter, false) let iter3 = iter4() @@ -718,8 +852,8 @@ template toSeq*(iter: untyped): untyped = let myRange = 1..5 mySet: set[int8] = {5'i8, 3, 1} - assert type(myRange) is HSlice[system.int, system.int] - assert type(mySet) is set[int8] + assert typeof(myRange) is HSlice[system.int, system.int] + assert typeof(mySet) is set[int8] let mySeq1 = toSeq(myRange) @@ -736,14 +870,14 @@ template toSeq*(iter: untyped): untyped = when compiles(iter.len): block: evalOnceAs(iter2, iter, true) - var result = newSeq[type(iter)](iter2.len) + var result = newSeq[typeof(iter)](iter2.len) var i = 0 for x in iter2: result[i] = x inc i result else: - var result: seq[type(iter)] = @[] + var result: seq[typeof(iter)] = @[] for x in iter: result.add(x) result @@ -754,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>`_ ## @@ -774,14 +908,21 @@ 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" - var result: type(s[0]) + var result: typeof(s[0]) result = s[0] for i in 1..<s.len: let @@ -793,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: @@ -809,8 +950,7 @@ template foldl*(sequence, operation, first): untyped = digits = foldl(numbers, a & (chr(b + ord('0'))), "") assert digits == "0815" - var result: type(first) - result = first + var result: typeof(first) = first for x in items(sequence): let a {.inject.} = result @@ -824,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 ## @@ -849,11 +989,11 @@ template foldr*(sequence, operation: untyped): untyped = assert multiplication == 495, "Multiplication is (5*(9*(11)))" assert concatenation == "nimiscool" - let s = sequence - assert s.len > 0, "Can't fold empty sequences" - var result: type(s[0]) - result = sequence[s.len - 1] - for i in countdown(s.len - 2, 0): + let s = sequence # xxx inefficient, use {.evalonce.} pending #13750 + let n = s.len + assert n > 0, "Can't fold empty sequences" + var result = s[n - 1] + for i in countdown(n - 2, 0): let a {.inject.} = s[i] b {.inject.} = result @@ -861,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 ## @@ -880,43 +1024,55 @@ 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 = type(( - block: - var it{.inject.}: type(items(s)); - op)) - when compiles(s.len): - block: # using a block avoids https://github.com/nim-lang/Nim/issues/8580 - - # BUG: `evalOnceAs(s2, s, false)` would lead to C compile errors - # (`error: use of undeclared identifier`) instead of Nim compile errors - evalOnceAs(s2, s, compiles((let _ = s))) + 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 + when compiles(s.len): + block: # using a block avoids https://github.com/nim-lang/Nim/issues/8580 + + # BUG: `evalOnceAs(s2, s, false)` would lead to C compile errors + # (`error: use of undeclared identifier`) instead of Nim compile errors + evalOnceAs(s2, s, compiles((let _ = s))) - var i = 0 - var result = newSeq[OutType](s2.len) - for it {.inject.} in s2: - result[i] = op - i += 1 + var i = 0 + var result = newSeq[OutType](s2.len) + for it {.inject.} in s2: + result[i] = op + i += 1 + result + else: + 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) result else: - var result: seq[OutType] = @[] - for it {.inject.} in s: - result.add(op) - result + # `op` is going to create closures in loops, let's fallback to `map`. + # NOTE: Without this fallback, developers have to define a helper function and + # call `map`: + # [1, 2].map((it) => ((x: int) => it + x)) + # 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`. + 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 + op + 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>`_ ## @@ -931,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[type(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) @@ -969,504 +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 - -when isMainModule: - import strutils - from algorithm import sorted - - # helper for testing double substitution side effects which are handled - # by `evalOnceAs` - var counter = 0 - proc identity[T](a: T): auto = - counter.inc - a - - block: # concat test - let - s1 = @[1, 2, 3] - s2 = @[4, 5] - s3 = @[6, 7] - total = concat(s1, s2, s3) - assert total == @[1, 2, 3, 4, 5, 6, 7] - - block: # count test - let - s1 = @[1, 2, 3, 2] - s2 = @['a', 'b', 'x', 'a'] - a1 = [1, 2, 3, 2] - a2 = ['a', 'b', 'x', 'a'] - r0 = count(s1, 0) - r1 = count(s1, 1) - r2 = count(s1, 2) - r3 = count(s2, 'y') - r4 = count(s2, 'x') - r5 = count(s2, 'a') - ar0 = count(a1, 0) - ar1 = count(a1, 1) - ar2 = count(a1, 2) - ar3 = count(a2, 'y') - ar4 = count(a2, 'x') - ar5 = count(a2, 'a') - assert r0 == 0 - assert r1 == 1 - assert r2 == 2 - assert r3 == 0 - assert r4 == 1 - assert r5 == 2 - assert ar0 == 0 - assert ar1 == 1 - assert ar2 == 2 - assert ar3 == 0 - assert ar4 == 1 - assert ar5 == 2 - - block: # cycle tests - let - a = @[1, 2, 3] - b: seq[int] = @[] - c = [1, 2, 3] - - doAssert a.cycle(3) == @[1, 2, 3, 1, 2, 3, 1, 2, 3] - doAssert a.cycle(0) == @[] - #doAssert a.cycle(-1) == @[] # will not compile! - doAssert b.cycle(3) == @[] - doAssert c.cycle(3) == @[1, 2, 3, 1, 2, 3, 1, 2, 3] - doAssert c.cycle(0) == @[] - - block: # repeat tests - assert repeat(10, 5) == @[10, 10, 10, 10, 10] - assert repeat(@[1, 2, 3], 2) == @[@[1, 2, 3], @[1, 2, 3]] - assert repeat([1, 2, 3], 2) == @[[1, 2, 3], [1, 2, 3]] - - block: # deduplicates test - let - dup1 = @[1, 1, 3, 4, 2, 2, 8, 1, 4] - dup2 = @["a", "a", "c", "d", "d"] - dup3 = [1, 1, 3, 4, 2, 2, 8, 1, 4] - dup4 = ["a", "a", "c", "d", "d"] - unique1 = deduplicate(dup1) - unique2 = deduplicate(dup2) - unique3 = deduplicate(dup3) - unique4 = deduplicate(dup4) - unique5 = deduplicate(dup1.sorted, true) - unique6 = deduplicate(dup2, true) - unique7 = deduplicate(dup3.sorted, true) - unique8 = deduplicate(dup4, true) - assert unique1 == @[1, 3, 4, 2, 8] - assert unique2 == @["a", "c", "d"] - assert unique3 == @[1, 3, 4, 2, 8] - assert unique4 == @["a", "c", "d"] - assert unique5 == @[1, 2, 3, 4, 8] - assert unique6 == @["a", "c", "d"] - assert unique7 == @[1, 2, 3, 4, 8] - assert unique8 == @["a", "c", "d"] - - block: # zip test - let - short = @[1, 2, 3] - long = @[6, 5, 4, 3, 2, 1] - words = @["one", "two", "three"] - ashort = [1, 2, 3] - along = [6, 5, 4, 3, 2, 1] - awords = ["one", "two", "three"] - zip1 = zip(short, long) - zip2 = zip(short, words) - zip3 = zip(ashort, along) - assert zip1 == @[(1, 6), (2, 5), (3, 4)] - assert zip2 == @[(1, "one"), (2, "two"), (3, "three")] - assert zip3 == @[(1, 6), (2, 5), (3, 4)] - assert zip1[2][1] == 4 - assert zip2[2][1] == "three" - assert zip3[2][1] == 4 - when (NimMajor, NimMinor) <= (1, 0): - let - # In Nim 1.0.x and older, zip returned a seq of tuple strictly - # with fields named "a" and "b". - zipAb = zip(ashort, awords) - assert zipAb == @[(a: 1, b: "one"), (2, "two"), (3, "three")] - assert zipAb[2].b == "three" - else: - let - # As zip returns seq of anonymous tuples, they can be assigned - # to any variable that's a sequence of named tuples too. - zipXy: seq[tuple[x: int, y: string]] = zip(ashort, awords) - zipMn: seq[tuple[m: int, n: string]] = zip(ashort, words) - assert zipXy == @[(x: 1, y: "one"), (2, "two"), (3, "three")] - assert zipMn == @[(m: 1, n: "one"), (2, "two"), (3, "three")] - assert zipXy[2].y == "three" - assert zipMn[2].n == "three" - - block: # distribute tests - let numbers = @[1, 2, 3, 4, 5, 6, 7] - doAssert numbers.distribute(3) == @[@[1, 2, 3], @[4, 5], @[6, 7]] - doAssert numbers.distribute(6)[0] == @[1, 2] - doAssert numbers.distribute(6)[5] == @[7] - let a = @[1, 2, 3, 4, 5, 6, 7] - doAssert a.distribute(1, true) == @[@[1, 2, 3, 4, 5, 6, 7]] - doAssert a.distribute(1, false) == @[@[1, 2, 3, 4, 5, 6, 7]] - doAssert a.distribute(2, true) == @[@[1, 2, 3, 4], @[5, 6, 7]] - doAssert a.distribute(2, false) == @[@[1, 2, 3, 4], @[5, 6, 7]] - doAssert a.distribute(3, true) == @[@[1, 2, 3], @[4, 5], @[6, 7]] - doAssert a.distribute(3, false) == @[@[1, 2, 3], @[4, 5, 6], @[7]] - doAssert a.distribute(4, true) == @[@[1, 2], @[3, 4], @[5, 6], @[7]] - doAssert a.distribute(4, false) == @[@[1, 2], @[3, 4], @[5, 6], @[7]] - doAssert a.distribute(5, true) == @[@[1, 2], @[3, 4], @[5], @[6], @[7]] - doAssert a.distribute(5, false) == @[@[1, 2], @[3, 4], @[5, 6], @[7], @[]] - doAssert a.distribute(6, true) == @[@[1, 2], @[3], @[4], @[5], @[6], @[7]] - doAssert a.distribute(6, false) == @[ - @[1, 2], @[3, 4], @[5, 6], @[7], @[], @[]] - doAssert a.distribute(8, false) == a.distribute(8, true) - doAssert a.distribute(90, false) == a.distribute(90, true) - var b = @[0] - for f in 1 .. 25: b.add(f) - doAssert b.distribute(5, true)[4].len == 5 - doAssert b.distribute(5, false)[4].len == 2 - - block: # map test - let - numbers = @[1, 4, 5, 8, 9, 7, 4] - anumbers = [1, 4, 5, 8, 9, 7, 4] - m1 = map(numbers, proc(x: int): int = 2*x) - m2 = map(anumbers, proc(x: int): int = 2*x) - assert m1 == @[2, 8, 10, 16, 18, 14, 8] - assert m2 == @[2, 8, 10, 16, 18, 14, 8] - - block: # apply test - var a = @["1", "2", "3", "4"] - apply(a, proc(x: var string) = x &= "42") - assert a == @["142", "242", "342", "442"] - - block: # filter proc test - let - colors = @["red", "yellow", "black"] - acolors = ["red", "yellow", "black"] - f1 = filter(colors, proc(x: string): bool = x.len < 6) - f2 = filter(colors) do (x: string) -> bool: x.len > 5 - f3 = filter(acolors, proc(x: string): bool = x.len < 6) - f4 = filter(acolors) do (x: string) -> bool: x.len > 5 - assert f1 == @["red", "black"] - assert f2 == @["yellow"] - assert f3 == @["red", "black"] - assert f4 == @["yellow"] - - block: # filter iterator test - let numbers = @[1, 4, 5, 8, 9, 7, 4] - let anumbers = [1, 4, 5, 8, 9, 7, 4] - assert toSeq(filter(numbers, proc (x: int): bool = x mod 2 == 0)) == - @[4, 8, 4] - assert toSeq(filter(anumbers, proc (x: int): bool = x mod 2 == 0)) == - @[4, 8, 4] - - block: # keepIf test - var floats = @[13.0, 12.5, 5.8, 2.0, 6.1, 9.9, 10.1] - keepIf(floats, proc(x: float): bool = x > 10) - assert floats == @[13.0, 12.5, 10.1] - - block: # delete tests - 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) - assert outcome == dest, """\ - Deleting range 3-9 from [1,1,1,2,2,2,2,2,2,1,1,1,1,1] - is [1,1,1,1,1,1,1,1]""" - var x = @[1, 2, 3] - x.delete(100, 100) - assert x == @[1, 2, 3] - - block: # insert tests - var dest = @[1, 1, 1, 1, 1, 1, 1, 1] - let - src = @[2, 2, 2, 2, 2, 2] - outcome = @[1, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1] - dest.insert(src, 3) - assert dest == outcome, """\ - Inserting [2,2,2,2,2,2] into [1,1,1,1,1,1,1,1] - at 3 is [1,1,1,2,2,2,2,2,2,1,1,1,1,1]""" - - block: # filterIt test - let - temperatures = @[-272.15, -2.0, 24.5, 44.31, 99.9, -113.44] - acceptable = filterIt(temperatures, it < 50 and it > -10) - notAcceptable = filterIt(temperatures, it > 50 or it < -10) - assert acceptable == @[-2.0, 24.5, 44.31] - assert notAcceptable == @[-272.15, 99.9, -113.44] - - block: # keepItIf test - var candidates = @["foo", "bar", "baz", "foobar"] - keepItIf(candidates, it.len == 3 and it[0] == 'b') - assert candidates == @["bar", "baz"] - - block: # all - let - numbers = @[1, 4, 5, 8, 9, 7, 4] - anumbers = [1, 4, 5, 8, 9, 7, 4] - len0seq: seq[int] = @[] - assert all(numbers, proc (x: int): bool = return x < 10) == true - assert all(numbers, proc (x: int): bool = return x < 9) == false - assert all(len0seq, proc (x: int): bool = return false) == true - assert all(anumbers, proc (x: int): bool = return x < 10) == true - assert all(anumbers, proc (x: int): bool = return x < 9) == false - - block: # allIt - let - numbers = @[1, 4, 5, 8, 9, 7, 4] - anumbers = [1, 4, 5, 8, 9, 7, 4] - len0seq: seq[int] = @[] - assert allIt(numbers, it < 10) == true - assert allIt(numbers, it < 9) == false - assert allIt(len0seq, false) == true - assert allIt(anumbers, it < 10) == true - assert allIt(anumbers, it < 9) == false - - block: # any - let - numbers = @[1, 4, 5, 8, 9, 7, 4] - anumbers = [1, 4, 5, 8, 9, 7, 4] - len0seq: seq[int] = @[] - assert any(numbers, proc (x: int): bool = return x > 8) == true - assert any(numbers, proc (x: int): bool = return x > 9) == false - assert any(len0seq, proc (x: int): bool = return true) == false - assert any(anumbers, proc (x: int): bool = return x > 8) == true - assert any(anumbers, proc (x: int): bool = return x > 9) == false - - block: # anyIt - let - numbers = @[1, 4, 5, 8, 9, 7, 4] - anumbers = [1, 4, 5, 8, 9, 7, 4] - len0seq: seq[int] = @[] - assert anyIt(numbers, it > 8) == true - assert anyIt(numbers, it > 9) == false - assert anyIt(len0seq, true) == false - assert anyIt(anumbers, it > 8) == true - assert anyIt(anumbers, it > 9) == false - - block: # toSeq test - block: - let - numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9] - oddNumbers = toSeq(filter(numeric) do (x: int) -> bool: - if x mod 2 == 1: - result = true) - assert oddNumbers == @[1, 3, 5, 7, 9] - - block: - doAssert [1, 2].toSeq == @[1, 2] - doAssert @[1, 2].toSeq == @[1, 2] - - doAssert @[1, 2].toSeq == @[1, 2] - doAssert toSeq(@[1, 2]) == @[1, 2] - - block: - iterator myIter(seed: int): auto = - for i in 0..<seed: - yield i - doAssert toSeq(myIter(2)) == @[0, 1] - - block: - iterator myIter(): auto {.inline.} = - yield 1 - yield 2 - - doAssert myIter.toSeq == @[1, 2] - doAssert toSeq(myIter) == @[1, 2] - - block: - iterator myIter(): int {.closure.} = - yield 1 - yield 2 - - doAssert myIter.toSeq == @[1, 2] - doAssert toSeq(myIter) == @[1, 2] - - block: - proc myIter(): auto = - iterator ret(): int {.closure.} = - yield 1 - yield 2 - result = ret - - doAssert myIter().toSeq == @[1, 2] - doAssert toSeq(myIter()) == @[1, 2] - - block: - proc myIter(n: int): auto = - var counter = 0 - iterator ret(): int {.closure.} = - while counter < n: - yield counter - counter.inc - result = ret - - block: - let myIter3 = myIter(3) - doAssert myIter3.toSeq == @[0, 1, 2] - block: - let myIter3 = myIter(3) - doAssert toSeq(myIter3) == @[0, 1, 2] - block: - # makes sure this does not hang forever - doAssert myIter(3).toSeq == @[0, 1, 2] - doAssert toSeq(myIter(3)) == @[0, 1, 2] - - block: - # tests https://github.com/nim-lang/Nim/issues/7187 - counter = 0 - let ret = toSeq(@[1, 2, 3].identity().filter(proc (x: int): bool = x < 3)) - doAssert ret == @[1, 2] - doAssert counter == 1 - block: # foldl tests - let - numbers = @[5, 9, 11] - addition = foldl(numbers, a + b) - subtraction = foldl(numbers, a - b) - multiplication = foldl(numbers, a * b) - words = @["nim", "is", "cool"] - concatenation = foldl(words, a & b) - 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" - - block: # foldr tests - let - numbers = @[5, 9, 11] - addition = foldr(numbers, a + b) - subtraction = foldr(numbers, a - b) - multiplication = foldr(numbers, a * b) - words = @["nim", "is", "cool"] - concatenation = foldr(words, a & b) - assert addition == 25, "Addition is (5+(9+(11)))" - assert subtraction == 7, "Subtraction is (5-(9-(11)))" - assert multiplication == 495, "Multiplication is (5*(9*(11)))" - assert concatenation == "nimiscool" - - block: # mapIt + applyIt test - counter = 0 - var - nums = @[1, 2, 3, 4] - strings = nums.identity.mapIt($(4 * it)) - doAssert counter == 1 - nums.applyIt(it * 3) - assert nums[0] + nums[3] == 15 - assert strings[2] == "12" - - block: # newSeqWith tests - var seq2D = newSeqWith(4, newSeq[bool](2)) - seq2D[0][0] = true - seq2D[1][0] = true - seq2D[0][1] = true - doAssert seq2D == @[@[true, true], @[true, false], @[false, false], @[false, false]] - - block: # mapLiterals tests - let x = mapLiterals([0.1, 1.2, 2.3, 3.4], int) - doAssert x is array[4, int] - doAssert mapLiterals((1, ("abc"), 2), float, nested = false) == - (float(1), "abc", float(2)) - doAssert mapLiterals(([1], ("abc"), 2), `$`, nested = true) == - (["1"], "abc", "2") - - block: # mapIt with openArray - counter = 0 - proc foo(x: openArray[int]): seq[int] = x.mapIt(it * 10) - doAssert foo([identity(1), identity(2)]) == @[10, 20] - doAssert counter == 2 - - block: # mapIt with direct openArray - proc foo1(x: openArray[int]): seq[int] = x.mapIt(it * 10) - counter = 0 - doAssert foo1(openArray[int]([identity(1), identity(2)])) == @[10, 20] - doAssert counter == 2 - - # Corner cases (openArray literals should not be common) - template foo2(x: openArray[int]): seq[int] = x.mapIt(it * 10) - counter = 0 - doAssert foo2(openArray[int]([identity(1), identity(2)])) == @[10, 20] - # TODO: this fails; not sure how to fix this case - # doAssert counter == 2 - - counter = 0 - doAssert openArray[int]([identity(1), identity(2)]).mapIt(it) == @[1, 2] - # ditto - # doAssert counter == 2 - - block: # mapIt empty test, see https://github.com/nim-lang/Nim/pull/8584#pullrequestreview-144723468 - # NOTE: `[].mapIt(it)` is illegal, just as `let a = @[]` is (lacks type - # of elements) - doAssert: not compiles(mapIt(@[], it)) - doAssert: not compiles(mapIt([], it)) - doAssert newSeq[int](0).mapIt(it) == @[] - - block: # mapIt redifinition check, see https://github.com/nim-lang/Nim/issues/8580 - let s2 = [1, 2].mapIt(it) - doAssert s2 == @[1, 2] - - block: - counter = 0 - doAssert [1, 2].identity().mapIt(it*2).mapIt(it*10) == @[20, 40] - # https://github.com/nim-lang/Nim/issues/7187 test case - doAssert counter == 1 - - block: # mapIt with invalid RHS for `let` (#8566) - type X = enum - A, B - doAssert mapIt(X, $it) == @["A", "B"] - - block: - # bug #9093 - let inp = "a:b,c:d" - - let outp = inp.split(",").mapIt(it.split(":")) - doAssert outp == @[@["a", "b"], @["c", "d"]] - - - block: - proc iter(len: int): auto = - result = iterator(): int = - for i in 0..<len: - yield i - - doAssert: iter(3).mapIt(2*it).foldl(a + b) == 6 - - when not defined(testing): - echo "Finished doc tests" |