@@ -95,14 +95,17 @@ Descartes is a famous philosopher from France.
9595# # ----------------------
9696# #
9797# # **Adaptors** return a new iterable and can be chained indefinitely:
98- # # `map`_, `mapIt`_, `filter`_, `filterIt`_, `skip`_, `skipWhile`_,
98+ # # `map<#map.t,iterable[T],proc(T)> `_, `mapIt`_, `filter`_, `filterIt`_, `skip`_, `skipWhile`_,
9999# # `skipWhileIt`_, `take`_, `takeWhile`_, `takeWhileIt`_, `stepBy`_,
100100# # `enumerate`_, `group`_, `flatten`_.
101101# #
102102# # **Consumers** drive evaluation and return a plain value:
103- # # `collect`_, `fold`_, `foldIt`_, `sum`_, `product`_, `count`_,
103+ # # `collect<#collect.t,iterable[T],Natural>`_,
104+ # # `fold<#fold.t,iterable[T],U,proc(sinkU,T)>`_, `foldIt`_, `sum`_, `product`_, `count`_,
104105# # `min`_, `max`_, `any`_, `anyIt`_, `all`_, `allIt`_,
105- # # `find`_, `findIt`_, `position`_, `positionIt`_, `nth`_.
106+ # # `find<#find.t,iterable[T],proc(T)>`_, `findIt<findIt.t,iterable[T]>`_,
107+ # # `position<#position.t,iterable[T],proc(T)>`_,
108+ # # `positionIt<#positionIt.t,iterable[T],untyped>`_, `nth`_.
106109# #
107110# # Adaptor templates named `...It` inject the `it` symbol in the inner
108111# # scope for the current element. `foldIt` also injects `acc` for the
@@ -170,8 +173,9 @@ runnableExamples:
170173# # -------
171174# #
172175# # #. Iterators can only be consumed once. Unlike a `seq`, an iterator has no
173- # # rewind. If you need to traverse the same data more than once, `collect`_
174- # # to a `seq` first and restart from `.items`.
176+ # # rewind. If you need to traverse the same data more than once,
177+ # # `collect<collect.t,iterable[T],Natural>`_ to a `seq` first and restart
178+ # # from `.items`.
175179# #
176180# # #. `foldIt`_ expects an expression, not a statement. The argument must
177181# # evaluate to the new accumulated value, which is assigned back to `acc`
@@ -180,11 +184,11 @@ runnableExamples:
180184# # `proc(acc: var U; it: T)` instead.
181185# #
182186# # #. Closure iterators are not accepted by `iterable[T]`. A value of type
183- # # `iterator(): T {.closure.}` does not satisfy the `iterable[T]` type.
184- # # Only direct inline-iterator calls - including `.items`, `.pairs`, and
185- # # named iterators such as `countUp` or `split` - work as pipeline sources.
186- # # If you hold a closure iterator value, wrap it in a helper inline iterator
187- # # that yields from it to use itertools.
187+ # # `iterator(): T {.closure.}` does not satisfy the `iterable[T]` type.
188+ # # Only direct inline-iterator calls - including `.items`, `.pairs`, and
189+ # # named iterators such as `countUp` or `split` - work as pipeline sources.
190+ # # If you hold a closure iterator value, wrap it in a helper inline iterator
191+ # # that yields from it to use itertools.
188192# #
189193# # See also
190194# # --------
@@ -250,9 +254,34 @@ macro genIter*[T](iter: iterable[T], body: varargs[untyped]): untyped =
250254 name ()
251255
252256type
257+ Comparable * = concept
258+ proc `<` (x, y: Self ): bool
259+ Summable * = concept # Additive?
260+ proc `+` (a, b: Self ): Self
261+ Multipliable * = concept # Multiplicative?
262+ proc `*` (a, b: Self ): Self
253263 Iterable * [T] = concept
254264 iterator items (a: Self ): T
255265
266+ Addable * [T] = concept
267+ proc add (x: var Self ; a: T)
268+ Includable * [T] = concept
269+ proc incl (x: var Self ; a: T)
270+ Pushable * [T] = concept
271+ proc push (x: var Self ; a: T)
272+
273+ Growable * [T] = Addable [T] | Includable [T] | Pushable [T]
274+ # # Matches any mutable container that supports adding elements via
275+ # # `add`, `incl` or `push`.
276+
277+ AssociativeContainer * [K, V] = concept
278+ # # Matches any mutable container that supports key-value assignment via
279+ # # `[]=`. This includes `Table[K, V]`, `TableRef[K, V]`,
280+ # # `OrderedTable[K, V]`, `strtabs.StringTableRef`
281+ # # (with `K = string`, `V = string`), and any user-defined type that
282+ # # provides a matching `[]=` operator.
283+ proc `[]=` (x: var Self ; key: K; val: V)
284+
256285# --------------------------------------------------------------------------
257286# Adaptors
258287# --------------------------------------------------------------------------
@@ -526,19 +555,11 @@ else:
526555# Consumers
527556# --------------------------------------------------------------------------
528557
529- type
530- Comparable * = concept
531- proc `<` (x, y: Self ): bool
532- Summable * = concept # Additive?
533- proc `+` (a, b: Self ): Self
534- Multipliable * = concept # Multiplicative?
535- proc `*` (a, b: Self ): Self
536-
537558template collect * [T](iter: iterable [T]; capacityHint: Natural = 1 ): seq [T] =
538559 # # Collects the elements of the iterator into a new sequence.
539560 # #
540561 # # For collecting into the user-specified type of container, see
541- # # `collect<#collect.t,iterable[T],typedesc[U ]>`_.
562+ # # `collect<#collect.t,iterable[T],typedesc[C ]>`_.
542563 # #
543564 # # .. Note:: `capacityHint` is optional and can be used to provide an initial
544565 # # capacity for the sequence.
@@ -552,34 +573,77 @@ template collect*[T](iter: iterable[T]; capacityHint: Natural = 1): seq[T] =
552573 val.add x
553574 val
554575
555- template collect * [T, U] (iter: iterable [T]; t : typeDesc [U ]): U =
576+ template collect * [T; C: Growable [T]] (iter: iterable [T]; toType : typeDesc [C ]): C =
556577 # # Collects the elements of the iterator into a new container.
557578 # #
558579 # # `collect` creates a new collection and fills it with the first available
559580 # # proc of `add`, `incl` or `push`. The resulting collection is returned.
560581 # #
561- # # .. Note:: The type `U ` should be compatible with the elements in the iterator.
562- # # If `U ` is a reference type, a new instance is created. Otherwise, the
563- # # default value of `U ` is used.
582+ # # .. Note:: The type `C ` should be compatible with the elements in the iterator.
583+ # # If `C ` is a reference type, a new instance is created. Otherwise, the
584+ # # default value of `C ` is used.
564585 # #
565586 runnableExamples:
566587 let nums = [1 , 2 , 3 , 4 , 5 ]
567588 let minusOne = nums.items.mapIt (it - 1 ).collect (seq [int ])
568- assert minusOne == @ [0 , 1 , 2 , 3 , 4 ]
569- # TODO ? Replace `when compiles` with specialisation on concepts
570- var acc = when t is ref :
571- new t
589+ doAssert minusOne == @ [0 , 1 , 2 , 3 , 4 ]
590+
591+ import std/ stats
592+ let rs = [10.0 , 20.0 , 30.0 ].items.collect (RunningStat )
593+ doAssert rs.sum == 60.0
594+ doAssert rs.mean == 20.0
595+ var acc = when C is ref :
596+ new C
572597 else :
573- default t
598+ default C
574599 for x in iter:
575- when compiles ( acc. add ( default ( typeOf (T)))) :
600+ when acc is Addable :
576601 acc.add x
577- elif compiles ( acc. incl ( default ( typeOf (T)))) :
602+ elif acc is Includable :
578603 acc.incl x
579- elif compiles ( acc. push ( default ( typeOf (T)))) :
604+ elif acc is Pushable :
580605 acc.push x
581606 acc
582607
608+ template collect * [K, V; C: AssociativeContainer [K, V]](
609+ iter: iterable [(K, V)]; toType: typedesc [C]): C =
610+ # # Collects a `(key, value)` iterator into an associative container.
611+ # #
612+ # # Each element yielded by `iter` must be a 2-tuple `(K, V)`.
613+ # # A fresh instance of `C` is created and populated by assigning each
614+ # # tuple as `container[key] = value`.
615+ # #
616+ # # `C` must satisfy `AssociativeContainer[K, V]<#AssociativeContainer>`_,
617+ # # meaning it provides ``proc \`[]=\`(c: var C; key: K; val: V)``.
618+ # # All standard library table types qualify.
619+ # #
620+ runnableExamples:
621+ import std/ [tables, strutils]
622+
623+ # Collect (word, length) pairs into a Table.
624+ let wordLens = [" one" , " two" , " three" ].items
625+ .mapIt ((it, it.len))
626+ .collect (Table [string , int ])
627+ doAssert wordLens[" three" ] == 5
628+
629+ # Build a reverse-lookup table from an enum.
630+ type Color = enum Red , Green , Blue
631+ let byName = [Red , Green , Blue ].items
632+ .mapIt (($ it, it))
633+ .collect (Table [string , Color ])
634+ doAssert byName[" Green" ] == Green
635+
636+ # Collect into an OrderedTable, preserving insertion order.
637+ import std/ algorithm
638+ let ordered = [(3 , " c" ), (1 , " a" ), (2 , " b" )].items
639+ .collect (OrderedTable [int , string ])
640+ doAssert ordered.values.collect () == @ [" c" , " a" , " b" ]
641+
642+ var acc = when C is ref : new C else : default (C)
643+ for (k, v) in iter:
644+ acc[k]= v
645+ acc
646+
583647template fold * [T, U](iter: iterable [T]; init: U; fn: proc (acc: sink U; it: T): U): U =
584648 # # Accumulates the values of the iterator using an accumulation function `fn`.
585649 # # This operation is also commonly known as "reduce" and is useful for
0 commit comments