Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 3 commits into from
Nov 22, 2018
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 130 additions & 30 deletions lib/pure/collections/sequtils.nim
Original file line number Diff line number Diff line change
Expand Up @@ -504,36 +504,76 @@ template anyIt*(s, pred: untyped): bool =
break
result

template toSeq*(iter: untyped): untyped =
## Transforms any iterator into a sequence.
##
## Example:
##
## .. code-block::
## let
## numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9]
## odd_numbers = toSeq(filter(numeric) do (x: int) -> bool:
## if x mod 2 == 1:
## result = true)
## assert odd_numbers == @[1, 3, 5, 7, 9]

# Note: see also `mapIt` for explanation of some of the implementation
# subtleties.
when compiles(iter.len):
template toSeq1(s: not iterator): untyped =
# overload for typed but not iterator
type outType = type(items(s))
when compiles(s.len):
block:
evalOnceAs(iter2, iter, true)
var result = newSeq[type(iter)](iter2.len)
evalOnceAs(s2, s, compiles((let _ = s)))
var i = 0
for x in iter2:
result[i] = x
inc i
var result = newSeq[outType](s2.len)
for it in s2:
result[i] = it
i += 1
result
else:
var result: seq[type(iter)] = @[]
for x in iter:
result.add(x)
var result: seq[outType] = @[]
for it in s:
result.add(it)
result

template toSeq2(iter: iterator): untyped =
# 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):
block:
evalOnceAs(iter2, iter, true)
var result = newSeq[type(iter)](iter2.len)
var i = 0
for x in iter2:
result[i] = x
inc i
result
else:
var result: seq[type(iter)] = @[]
for x in iter:
result.add(x)
result

template foldl*(sequence, operation: untyped): untyped =
## Template to fold a sequence from left to right, returning the accumulation.
##
Expand Down Expand Up @@ -1027,12 +1067,72 @@ when isMainModule:
assert anyIt(anumbers, it > 9) == false

block: # toSeq test
let
numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9]
odd_numbers = toSeq(filter(numeric) do (x: int) -> bool:
if x mod 2 == 1:
result = true)
assert odd_numbers == @[1, 3, 5, 7, 9]
block:
let
numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9]
odd_numbers = toSeq(filter(numeric) do (x: int) -> bool:
if x mod 2 == 1:
result = true)
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:
# tests https://github.com/nim-lang/Nim/issues/7187
Expand Down