Hi,
I had some problems with the forum (the title changes if I open a thread). I hope I didn't mess up anything.
So here is my question. The filter iterator is lazy, which is great (the cycle might be interrupted by a break or a return). However the filterIt seems to generate the whole list in advance. Is there a reason for this?
Thanks, Peter
Example:
var y = 0
for x in filter(@[1,2,3,4,5,6,7,8,9,10], proc(x: int): bool = x mod 2==y):
echo x
y = 1
# result: 2, 3, 5, 7, 9
Where filterIt generates the list in advance:
var y = 0
for x in filterIt(@[1,2,3,4,5,6,7,8,9,10], it mod 2==y):
echo x
y = 1
# result: 2, 4, 6, 8, 10
I've tried to create a template which produces an iterator. This is the closest I've got:
template myFilterIt*[T](seq1: seq[T]; pred: expr): (iterator(): T) {.immediate.} =
iterator w(): int{.closure.}=
for it {.inject.} in seq1:
if pred:
yield it
w
var y = 0
var itClosure = myFilterIt(@[1,2,3,4,5,6,7,8,9,10], it mod 2==y)
for x in itClosure():
echo x
y = 1
#result: 2, 3, 5, 7, 9
There are several problems with this approach. I have to create an extra variable itClosure, otherwise it is not working. I've tried:
for x in myFilterIt(@[1,2,3,4,5,6,7,8,9,10], it mod 2==y)():
echo x
y =1
but it produces 1, 1, 1, 1, 1, 1, ... And also I can't put the for cycle into a different block, e.g.:
block:
var y = 0
var itClosure = myFilterIt(@[1,2,3,4,5,6,7,8,9,10], it mod 2==y)
for x in itClosure():
echo x
y = 1
because it fails to compile with an internal error (I couldn't get a useful message), it's only working if the condition is not containing the variable y (for example "it mod 2==1").Btw, I wrote an article about seq that you may find interesting (most of the meat is in the code): http://goran.krampe.se/2014/12/03/nim-seq
If you can live with lambda syntax instead of "it" style macro, then it can look like:
import sequtils, future
var y = 0
for x in filter(@[1,2,3,4,5,6,7,8,9,10], (x:int) => x mod 2==y):
echo x
y = 1
Hi,
I have so many questions, please indicate if this is not the right forum/form to ask.
I couldn't use the lambda syntax with => in it (says compile error), shall I update from version Version 0.11.0 (2015-05-03)?
Is it possible to build a sequence easily containing a range of numbers? I can use
echo lc[x | (x<-1..10), int]
but it would be great if one of the following would work:
echo @[1..10] #compile error
echo map([1:10],proc (x: int): int = x) #wrong answer, which is strange
echo filter([1:10],proc (x: int): bool = x mod 2==0) #compile error
Back to the iterators, is this behavior intended?
iterator orig():int {.closure.} =
yield(1)
yield(2)
var copied = orig # here orig() fails, so this is not "copied = orig()"
for i in orig():
echo "first orig", i
for i in orig():
echo "second orig", i
for i in copied():
echo "first copied", i
for i in copied():
echo "second copied", i
#result:
#first orig1
#first orig2
#second orig1
#second orig2
#first copied1
#first copied2
(Second orig1 and second orig2 is missing.) If this behavior was intended, then how can we reset a closure iterator? I've debugged the generated code and I can see that the environment is copied only once, but can we have more control over this?
Thanks, Peter
I couldn't use the lambda syntax with => in it (says compile error)
Don't forget to import future.
Is it possible to build a sequence easily containing a range of numbers?
import sequtils
var x = toSeq 1..10
echo x
> If this behavior was intended, then how can we reset a closure iterator?
Maybe this is what you want:
proc orig(): auto =
return iterator:int {.closure.} =
yield(1)
yield(2)
var copied = orig()
for i in copied():
echo "first copied", i
for i in copied():
echo "second copied", i
Regarding lambda syntax, did you import future?
Yes, I think everyone should upgrade to 0.11.2, the 0.11.0 is buggy.
Try echo toSeq(1..10) to create a seq from an iterator. This is a bit tricksy, the .. does double duty. Either it creates Slices or it is an alias for countup(), see: http://nim-lang.org/docs/system.html#...i,S,T
Regarding other seq tricks, did you read my article I linked?
This code works:
var s = toSeq(1..10) # Create a seq from an iterator
echo(s[3..5]) # Use a slice of the seq
echo map(s, proc (x: int): int = x)
echo filter(s, proc (x: int): bool = x mod 2==0)
# Result:
# @[4, 5, 6]
# @[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# @[2, 4, 6, 8, 10]
Regarding copying an iterator... dunno.
Hi def, gokr,
Thanks for the explanations. I've read gokr's article, got famailiar with toSeq. I love the => syntax. It took me some time to realize that I can use "if" inside the expression with an extra parenthesis:
echo map(toSeq 1..10, (x: int) => if x > 5: 2*x else: x) # fails to compile
echo map(toSeq 1..10, (x: int) => (if x > 5: 2*x else: x)) # works fine
So I love this.
I'm still missing some basic concepts, like copying iterators, writing template which returns an iterator, and why map(1..10, f) returns the wrong result. I'll keep readying and trying.
I'm planing to understand the iterators better to understand whether they have any drawbacks. My motivation is to see whether we can achieve the same laziness that Clojure has: filter and map are lazy by default. I understand that map can be faster if we allocate the right sequence size in advance.
Thanks again, Peter