universal toSeq: works with UFCS; works with inline & closure iterators, and with iterables (#8711)

* universal toSeq: works with UFCS; works with inline, closure, and proc
iterators, and also non-iterators

* support all iterables with toSeq

* workaround for #9130
This commit is contained in:
Timothee Cour 2018-11-22 02:47:14 -08:00 • committed by Arne Döring
commit 02351d02e7

View file

@ -504,21 +504,61 @@ template anyIt*(s, pred: untyped): bool =
break break
result result
template toSeq*(iter: untyped): untyped = template toSeq1(s: not iterator): untyped =
## Transforms any iterator into a sequence. # overload for typed but not iterator
## type outType = type(items(s))
## Example: when compiles(s.len):
## block:
## .. code-block:: evalOnceAs(s2, s, compiles((let _ = s)))
## let var i = 0
## numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9] var result = newSeq[outType](s2.len)
## odd_numbers = toSeq(filter(numeric) do (x: int) -> bool: for it in s2:
## if x mod 2 == 1: result[i] = it
## result = true) i += 1
## assert odd_numbers == @[1, 3, 5, 7, 9] result
else:
var result: seq[outType] = @[]
for it in s:
result.add(it)
result
# Note: see also `mapIt` for explanation of some of the implementation template toSeq2(iter: iterator): untyped =
# subtleties. # overload for iterator
evalOnceAs(iter2, iter(), false)
when compiles(iter2.len):
var i = 0
var result = newSeq[type(iter2)](iter2.len)
for x in iter2:
result[i] = x
inc i
result
else:
type outType = type(iter2())
var result: seq[outType] = @[]
when compiles(iter2()):
evalOnceAs(iter4, iter, false)
let iter3=iter4()
for x in iter3():
result.add(x)
else:
for x in iter2():
result.add(x)
result
template toSeq*(iter: untyped): untyped =
## Transforms any iterable into a sequence.
runnableExamples:
let
numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9]
odd_numbers = toSeq(filter(numeric, proc(x: int): bool = x mod 2 == 1))
doAssert odd_numbers == @[1, 3, 5, 7, 9]
when compiles(toSeq1(iter)):
toSeq1(iter)
elif compiles(toSeq2(iter)):
toSeq2(iter)
else:
# overload for untyped, eg: `toSeq(myInlineIterator(3))`
when compiles(iter.len): when compiles(iter.len):
block: block:
evalOnceAs(iter2, iter, true) evalOnceAs(iter2, iter, true)
@ -1033,6 +1073,7 @@ when isMainModule:
assert anyIt(anumbers, it > 9) == false assert anyIt(anumbers, it > 9) == false
block: # toSeq test block: # toSeq test
block:
let let
numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9] numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9]
odd_numbers = toSeq(filter(numeric) do (x: int) -> bool: odd_numbers = toSeq(filter(numeric) do (x: int) -> bool:
@ -1040,6 +1081,65 @@ when isMainModule:
result = true) result = true)
assert odd_numbers == @[1, 3, 5, 7, 9] assert odd_numbers == @[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: block:
# tests https://github.com/nim-lang/Nim/issues/7187 # tests https://github.com/nim-lang/Nim/issues/7187
counter = 0 counter = 0